Revision 15, 20 October 2026, for docs/11 revision 26 (OpenAPI 2.12.6). The FIX 4.4 order-entry, barrier RFQ and drop-copy gateway of the barriers Partner API, for a partner that integrates over FIX beside the REST routes and the webhooks of docs/11-partner-api.md. The gateway maps onto exactly the same router functions, pre-trade checks, sentences, codes and hook sites as the REST routes: whatever FIX answers, REST and the webhooks would answer about the same fact, and the three transports never disagree. This document is served at https://dev.brokerage.perpetuals.com/fix beside the Partner API reference at /docs. The tag tables marked as generated and the BodyLength(9), CheckSum(10) and SendingTime(52) of every example frame are produced from the router's own code and pinned by our tests (Appendix A), so the examples can be fed to a FIX engine as they stand.
Status: live in this revision are the session layer (section 1), the client attribution (section 2), order entry, cancel and status (section 3), the ExecutionReport stream (section 4), the drop-copy session and the routing of reports (section 5), the delivery semantics (section 6), the barrier RFQ vocabulary (section 7: 35=R, 35=S, 35=AJ, 35=Z, 35=AI and the two contract reports), the reject doctrine and the reply policy (section 8), the sessions panel of our operations desk and the fix block of GET /partner/v1/me. Staking swaps, margin, collateral and market data stay on the Partner API (section 9).
Endpoint: dev.brokerage.perpetuals.com:9850 on the development environment, the same hostname as the Partner API, over TLS with a publicly trusted certificate renewed automatically. Production connectivity (host, port) is confirmed by our operations at onboarding. There is no proxy in front of the FIX port: the address the router sees is your own, and your partner CIDR allowlist applies to it exactly as it does to the REST routes.
Contents
BeginString must be FIX.4.4. One TLS connection per session; the certificate is the publicly trusted certificate of the Partner API hostname, renewed automatically. The TLS handshake must complete within 5 seconds of the TCP connect or the connection is closed.Logout(35=5) with Text(58) logon timeout. The deadline is measured from the TCP accept and inbound bytes do NOT extend it, so connect only when you are ready to log on.Sign in again. answer. A silent close therefore tells a peer only whether its address is on some partner's list, which we accept.Logout session ceiling reached for this partner and 9110=4290. A partner holds at most ten keys, so the session ceiling binds before the key cap does.SenderCompID(49) is your PARTNER CODE (the upper-case handle of your partner record, SWISSBORG, NEOBROKER1), on every message; your TargetCompID(56) is BARRIERS. The router sends 49=BARRIERS and 56=<your code>. Before a Logon has passed its credential checks (the logon required Logout, a refused Logon, the malformed-framing Logout), the router addresses you as 56=CLIENT: 49 and 56 are standard header fields that FIX 4.4 requires on every message and that can therefore be neither omitted nor left empty, so a documented placeholder is the only valid answer there. A 56 other than BARRIERS on the Logon is refused by name (TargetCompID(56) must be BARRIERS); a 49 that is not the code of the key's partner is refused with the credential sentence below, which reveals nothing about the key.EncryptMethod(98)=0HeartBtInt(108) between 5 and 120 secondsResetSeqNumFlag(141)=Y (required on EVERY logon)Username(553) = the display prefix of your key, the ten characters pbk_ plus six that our operations desk and GET /partner/v1/me key.prefix show; Password(554) = the whole pbk_ key, over TLSMsgSeqNum(34)=19120=Y for a drop-copy session (section 5)Every one of those is checked and a failure closes the session with a Logout naming the reason, so a Logon is never partially honoured. The order of the checks: 98, 141, 108, 56, the credentials, 34, the partner ceiling, the one-session-per-key rule. The reply (35=A) echoes 98=0, 108, 141=Y and 9120 as applied (Y or N).
Logout(35=5) messages whose Text(58) is the REST sentence verbatim and whose 9110 is the REST code; the socket is closed after a short flush. The key must be ACTIVE and of scope TRADE, your partner record ACTIVE, and your address inside your allowlist.| Case | Text(58) | 9110 |
|---|---|---|
553 or 554 absent or empty; 554 malformed; 554 does not start with 553 (the two must describe one key); the key is unknown or revoked; the partner is CLOSED; your address is outside your allowlist; 49 is not your partner code | Sign in again. | 4010 |
| The partner is SUSPENDED | This partner is suspended. Contact your account manager. | 4037 |
| The key's scope is READ (FIX is order entry: every session needs a TRADE key, a drop-copy session included; a READ key can read the same facts over REST and the webhooks) | This partner key cannot use this endpoint. | 4034 |
| The logon throttle is engaged for the prefix or for your address | Sign in again. | 4010 |
| The fifth session of the partner | session ceiling reached for this partner | 4290 |
| A second Logon of a key whose session is live and healthy | session already active | none |
| Our sessions register could not be written (the row that makes the session visible to our operations desk is part of the Logon) | This service is not available on this router. | 5030 |
The unknown-key answer is ONE sentence for seven causes on purpose: neither the existence of a key nor its validity is revealed to an address off the allowlist, the same rule the REST routes keep (docs/11 section 3).
Sign in again. Logout before the key is even looked up. The prefix dimension is keyed on the caller's address on purpose: your key prefix is public (our operations desk, GET /partner/v1/me, your log notes), so a stranger anywhere on the internet can lock only itself out, never you. An IPv6 address counts as its /64. A successful Logon clears the pair's counter. A READ-key refusal does not count (the key is valid and the caller is its holder). The throttle is process-local and gone on a router restart; its aim is to bound guessing.35=D, 35=F, 35=R, 35=AJ and 35=Z accepted for processing counts against your partner's trade_per_minute bucket and every 35=H against read_per_minute, the SAME buckets as the REST routes (docs/11 section 3): a FIX order spends REST budget and a REST order spends FIX budget. A message refused at the session layer (Reject(35=3)) never reaches the meter. Over the limit the message's own business reject carries Too many requests. Please slow down and try again. with 9110=4290 (the shapes per message type in 8.2).0x7F is refused BY NAME before anything reads it, with Reject(35=3) SessionRejectReason(373)=6 (incorrect data format), RefTagID(371) = the tag, RefMsgType(372) and Text(58) non-ASCII value in tag <tag>: FIX tag values are ASCII; the session stays up and nothing is placed. A reference is therefore never silently rewritten (two references differing only in non-ASCII bytes can never read as one). Before the Logon such a byte is malformed framing (Logout malformed message: non-ASCII value in tag <tag>: FIX tag values are ASCII and the close). Your partnerClientRef obeys the same rule at onboarding (section 2).Logout session already active, UNLESS the live session has missed two heartbeats (the router has heard nothing from it for twice ITS OWN HeartBtInt, the same instant the heartbeat sweep would disconnect it): then the new Logon REPLACES it. The replaced session receives, best effort, Logout session replaced and is closed; the new session is registered before the old one's close completes, so the key never holds zero live sessions during a replacement. The check is on the live session's own interval, never the newcomer's, so a newcomer cannot shorten the window by declaring a short interval. Run one key per connection; a reconnect after a crash therefore waits at most twice the old session's HeartBtInt, or logs on with a second key at once.Logout Sign in again. (9110=4010), a suspended partner's sessions end with the suspended sentence (9110=4037), and a session whose address is no longer inside your allowlist (you or our operations narrowed the list after the Logon) ends with Logout Sign in again. (9110=4010) within the same interval: the allowlist applies to a live session exactly as it applies to every REST request, so narrowing it is the way to cut off a decommissioned or compromised egress address. The key's lastUsedAt (GET /partner/v1/me) is refreshed at the Logon and then at most once a minute while the session is live.Logout with the text MsgSeqNum {seq} where {expected} expected: FIX resend is not supported, re-logon with 141=Y and recover through GET /partner/v1/clients/{clientId}/orders/open and the order.*, rfq.* and barrier.closed webhooks and closes; a message with PossDupFlag(43)=Y and a MsgSeqNum(34) below the expected one is dropped silently (already processed); a SequenceReset(35=4) with NewSeqNo(36) above the expected number is honoured, one at or below it is ignored. An outbound ResendRequest(35=2) is answered with a GapFill SequenceReset(35=4) (GapFillFlag(123)=Y) that takes the next number in the router's own outbound sequence and points NewSeqNo(36) at the one after it. The router's MsgSeqNum(34) is strictly increasing for the life of a session and never renumbers. Recovery after any disconnect is the REST read plus the webhooks (section 6), which is complete, ordered and deduplicable across every transport.HeartBtInt: the router sends a Heartbeat(35=0) when it has sent nothing for HeartBtInt, a TestRequest(35=1) after 1.2x silence from you, and disconnects after 2x (Logout heartbeat timeout, best effort). Malformed framing (a bad checksum, a bad BeginString, 10= off the BodyLength boundary, a frame above the 64 KiB BodyLength cap or a reassembly window that fills without a parse) answers Logout malformed message: <reason> and closes: FIX cannot resynchronise mid-stream.TestRequest(35=1) must carry TestReqID(112). The answering Heartbeat has to echo it, so there is nothing legal for the router to send back when it is absent or empty: such a request answers a session-level Reject(35=3) with SessionRejectReason(373)=1, RefTagID(371)=112 and RefMsgType(372)=1, and no Heartbeat. The session stays up.Reject(35=3) naming the tag. That holds on the standard header too, which is why a session that has not passed its credential checks is addressed as CLIENT rather than with an empty 56, and it holds for a tag you sent EMPTY as well as for one you omitted: the router reads a present-but-empty field as one you did not send, on every message.Logout(35=5) with Text(58) router restart before the TCP connection is closed (best effort, a session whose outbound queue is full is closed at the same bounded deadline). Nothing is dropped for cause: reconnect with backoff, Logon again with 141=Y, and recover as after any other disconnect (section 6). Neither the session nor anything queued for it survives a restart.Logout disconnected by operations and is closed. The reason stays in our audit record and is never on the wire; ask your account manager.logon required, logon timeout, the five Logon shape sentences (EncryptMethod(98) must be 0: transport TLS only, ResetSeqNumFlag(141)=Y is required on every logon, HeartBtInt(108) must be 5..120 seconds, TargetCompID(56) must be BARRIERS, MsgSeqNum must be 1 on a 141=Y logon), the credential sentences of the table above, heartbeat timeout, slow consumer (the outbound queue of 4096 frames refused a report or a session message because you stopped reading), session replaced, session ceiling reached for this partner, malformed message: <reason>, the MsgSeqNum gap sentence, logout acknowledged (your own Logout confirmed, then the close), router restart, disconnected by operations.Logout(35=5) is confirmed with Logout logout acknowledged and the socket is closed after the flush. A peer Reject(35=3) is read and ignored.GET /partner/v1/me carries fix {enabled, port, sessions: [{keyPrefix, connectedAt, lastInboundAt}]}: whether the gateway is served on the host you reached, its port, and your live sessions (docs/11 section 8). enabled means our acceptor is LISTENING on that host: it reads false, with port null, while the acceptor is not listening (a start failure our operations are retrying), so a refused connection beside enabled: false is ours to fix, not yours. Use it to confirm a deployment from your side.Example Logon and reply (| stands for SOH; the key is an example of the real shape, pbk_ plus 43 URL-safe base64 characters, the first ten of which are the prefix sent in 553):
8=FIX.4.4|9=145|35=A|49=SWISSBORG|56=BARRIERS|34=1|52=20260908-09:00:00.000|98=0|108=30|141=Y|553=pbk_1f9c2d|554=pbk_1f9c2dQ7xL0mW3vR8kT2nY5bH9pC4sJ6aF1dG8eK0zM|10=110|
8=FIX.4.4|9=85|35=A|49=BARRIERS|56=SWISSBORG|34=1|52=20260908-09:00:00.010|98=0|108=30|141=Y|9120=N|10=154|
A refused Logon (a key of another partner, an address off the allowlist, an unknown key: one answer):
8=FIX.4.4|9=85|35=5|49=BARRIERS|56=CLIENT|34=1|52=20260908-09:00:00.010|58=Sign in again.|9110=4010|10=059|
Every business message (35=D, 35=F, 35=H, 35=R, 35=AJ, 35=Z) names the end client it acts for with the standard Parties component. A partner never trades for itself; every order and request is a client's.
| Tag | Value |
|---|---|
NoPartyIDs(453) | 1 |
PartyID(448) | your own partnerClientRef, or our client uid exactly as we issued it (opaque, docs/11 section 7; BARR-00000412 in the examples) |
PartyIDSource(447) | D (proprietary / custom code) |
PartyRole(452) | 3 (Client ID) |
The resolver is the REST routes' (GET /partner/v1/clients/{clientId} for the uid, GET /partner/v1/clients/by-ref/{ref} for the reference; both refuse a client of another partner as if it did not exist): a 448 that is one of our client uids exactly as we issued it resolves by uid, any other value resolves as your reference. Our client uid is opaque (docs/11 section 7, revision 25): store it as we return it and send it back exactly as issued, letter case and digits as served; no digit count or other format is promised, and it may change for clients created later. Another spelling of a uid (BARR-412, barr-00000412) is therefore read as a reference, and since no partnerClientRef starts with BARR- in any letter case, it is refused as an unknown client (the last row below). Our numeric clientId, which the REST routes accept, is NOT read as our id on FIX, because a partner reference may itself be numeric and one field cannot carry both readings: a bare number in 448 is read as your own partnerClientRef, so it acts for the client you onboarded under that reference when you hold one, and it is refused (the last row below) only when no client of yours carries that reference.
Refusals are a BusinessMessageReject(35=j) with RefSeqNum(45), RefMsgType(372) = the message type, BusinessRejectRefID(379) = the message's own reference when it carried one (ClOrdID(11) on 35=D, OrigClOrdID(41) else OrderID(37) on 35=F and 35=H, QuoteReqID(131) on 35=R and 35=Z, QuoteRespID(693) on 35=AJ), and Text(58):
| Case | 380 | Text(58) | 9110 |
|---|---|---|---|
No Parties component at all, or 453 absent or empty, or 448 absent or empty | 5 (conditionally required field missing) | The client is required (Parties, PartyRole 3). | none |
A component present but not the one shape (453 other than 1, 447 other than D, 452 other than 3, more than one 448) | 5 | Parties: exactly one party with PartyIDSource(447) D and PartyRole(452) 3. | none |
| No client of yours under the value: an unknown uid or reference, a client of another partner, another spelling of a uid, or a bare number that is not one of your references (the REST 404 code 4004) | 1 (unknown ID) | Unknown client. | 4004 |
A 35=j is a verdict on the MESSAGE: no order, request or cancel is built, nothing reaches the router's functions, no report follows, the session stays up. The resolver runs BEFORE the message's own required-tag checks on purpose: a partner sending orders for a client it does not hold hears that first, whatever else is wrong with the message. The Parties component rides on every report: a report the gateway builds in answer to your message (a refusal before the venue, a restatement, a replay, the creation report of a request) echoes the three tags with the value you sent; a report built at a hook site (the acknowledgement, the fills, the cancel, the quotes, the accept, the contract reports) carries 448 = the client's partnerClientRef when set, else our uid, on every session that receives it, because the hook site does not see the message that placed the order (4.1 and section 5). A partner that sends its own reference sees one value throughout; a partner that sends our uid for a client with a reference on file reads the reference on the hook-built reports.
Because the reference rides on the wire as PartyID(448), a partnerClientRef is printable ASCII (0x20 to 0x7E, the FIX String domain): POST /partner/v1/clients refuses any other character with the field sentence partnerClientRef must use printable ASCII characters only (0x20 to 0x7E): it is carried on FIX as PartyID(448). (docs/11 section 9). For the same reason a partnerClientRef never starts with BARR-, our own client uid namespace: a 448 that is one of our uids resolves by uid, so a reference in that namespace could name another of your clients on every message, and the hook-built reports would print a 448 we read as the wrong client. The same prefix also begins references of ours that are NOT client identifiers: our order reference BARR-{orderId} (the order id unpadded, BARR-4185, the venue reference of 3.2 and the OrderID(37) form BARR-4185 of 3.5 and 3.7), our barrier request reference BARR-RFQ-{rfqId} and our staking swap reference BARR-SWP-{swapId}; never send one in 448: it is not a client identifier. POST /partner/v1/clients refuses such a reference, the prefix reserved case-insensitively, with the field sentence partnerClientRef must not start with BARR-: that prefix is our own client uid namespace and PartyID(448) on FIX reads such a value as our uid. (the "never starting with BARR-" rule of 3.1 and 7.1, which now covers the client reference too).
8=FIX.4.4|9=151|35=D|49=SWISSBORG|56=BARRIERS|34=2|52=20260908-09:00:00.200|11=ord-1001|453=1|448=cust-001|447=D|452=3|55=BTC-USD-PERP|54=1|38=0.01|40=2|44=60000|59=1|10=029|
8=FIX.4.4|9=119|35=j|49=BARRIERS|56=SWISSBORG|34=2|52=20260908-09:00:00.200|45=2|372=D|379=ord-1001|380=1|58=Unknown client.|9110=4004|10=164|
Order entry over FIX goes through exactly the router function the REST route POST /partner/v1/clients/{clientId}/orders calls: every pre-trade check of docs/11 section 12.3 (the offered market and its family, the verification gate, restrictions and halts, the market controls, the lifecycle state, the shape of the order, the price and lot grid, the margin admission) runs unchanged and answers the SAME sentence, with the REST code in tag 9110. The order is attributed on your partner log stream and the client's with the words over FIX, and its record on GET .../orders and GET .../orders/open reads channel: "FIX", channelKeyId (the key of the session that placed it) and channelRef (your ClOrdID(11)).
"Named on absence" below means the tag is refused BY NAME with a session-level Reject(35=3) 373=1 371=<tag> 372=D when absent or empty, before an order is built (section 8.1): a field whose absence changes what the request MEANS is never guessed. "Default on absence" means absence selects the documented default. A PRESENT but invalid value keeps its business reject (3.4).
| Tag | Required | Type | Meaning |
|---|---|---|---|
ClOrdID(11) | named on absence | String, 1 to 64 characters, never starting with BARR- (our own venue namespace) | Your reference for the order, unique per CLIENT for 24 hours: it is the order's Idempotency-Key (3.3), it rides on every report as 11, and a cancel or a status request may name the order by it in OrigClOrdID(41). A reference of the wrong shape answers 150=8 Text(58) ClOrdID(11) must be 1 to 64 characters and must not start with BARR-. A reference that begins or ends with whitespace, or is whitespace only, is refused at the door the same way (150=8 103=99 9110=4000, the value echoed as sent) with Text(58) ClOrdID(11) begins or ends with whitespace and is refused, before the store of 3.3 is read or written: nothing is trimmed, so ABC is never read as ABC (revision 9). |
Parties (453, 448, 447, 452) | required (35=j) | section 2 | The client. |
Symbol(55) | named on absence | String | A PERP or SPOT market symbol (BTC-USD-PERP, BTC-USD). A barrier market answers 150=8 with the barrier-by-RFQ sentence (103=1): request quotes with 35=R. |
Side(54) | named on absence | Char | 1 Buy, 2 Sell. A value outside the FIX 4.4 Side enumeration (1 to 9, A to G) is refused by name with Reject(35=3) 373=5 (an echoed 54 would make the answering 35=8 invalid FIX); a value inside the enumeration the router does not trade keeps its business reject The side must be BUY or SELL. (103=11). |
OrderQty(38) | named on absence | Qty (decimal string) | The quantity, on the lot grid of the market. |
OrdType(40) | named on absence | Char | 1 Market, 2 Limit, 3 Stop, 4 Stop limit. With 9101 below, 3 = STOP_MARKET or TAKE_PROFIT_MARKET, 4 = STOP_LIMIT or TAKE_PROFIT_LIMIT. Any other value: 150=8 This order type is not available. (103=11). |
9101 TriggerKind | default on absence | String | STOP (default) or TAKE_PROFIT; read with 40=3 or 4 only (ignored otherwise). Any other value: 150=8 TriggerKind(9101) must be STOP or TAKE_PROFIT. |
Price(44) | conditional | Price | 40=2: the limit price. 40=4: the limit price of the triggered order, within the market's price band around StopPx(99), or around the mark once the mark has reached it (revision 15, the conditional limit band of 3.4). 40=1 and 40=3: must be absent (a sent price answers A market order takes no price., or the conditional-order sentence). |
StopPx(99) | conditional | Price | 40=3 or 4: the trigger price. Absent on a stop type: A STOP_MARKET order needs a trigger. (the sentence names the mapped type). Present on 40=1 or 2: A LIMIT order takes no trigger. (likewise). |
9103 TriggerSource | default on absence | String | LAST (default), MARK or INDEX: the price the trigger watches. |
9104 TriggerDirection | default on absence | String | AT_OR_ABOVE or AT_OR_BELOW. Absent, it is derived: a STOP buy triggers AT_OR_ABOVE, a STOP sell AT_OR_BELOW, a TAKE_PROFIT buy AT_OR_BELOW, a TAKE_PROFIT sell AT_OR_ABOVE. A value you DID send is never replaced. |
TimeInForce(59) | default on absence | Char | 1 GTC (default), 3 IOC, 4 FOK. 0 (DAY, the FIX default) and every other value: 150=8 The time in force must be one of GTC, IOC, FOK or POST_ONLY. (absence selects the default; a sent value is never substituted). |
ExecInst(18) contains 6 | optional | MultipleValueString | Post-only: the time in force becomes POST_ONLY; needs 40=2, else Post-only applies to limit orders only.. |
9102 ReduceOnly | default on absence | Char | Y = reduce-only (the order may only shrink the client's position); N or absent = not. Any other value: 150=8 ReduceOnly(9102) must be Y or N. |
TransactTime(60) | optional | UTCTimestamp | Accepted and ignored: the router timestamps from its own clock. |
Account(1) | optional | String | Accepted and ignored (the client is the Parties component), so an off-the-shelf stack that always writes it is not refused. |
The five named-on-absence tags are checked in the order 11, 55, 54, 38, 40, after the client resolver of section 2, so the tag you hear about first is stable. Nothing else is required: a LIMIT GTC needs 11, the Parties component, 55, 54, 38, 40=2 and 44.
Not carried over FIX: attached take-profit and stop-loss legs, OCO groups, the leverage hint and a note (place brackets over REST; section 9).
The router places the order at the venue under its own reference (BARR-<orderId>) and reports to your session through the hook sites of section 4:
35=8 150=0 39=0 from the same site that produces the order.accepted webhook, with our OrderID(37), your ClOrdID(11), LeavesQty(151) = the quantity, 9113=ACKED (or PENDING_TRIGGER for a conditional order the venue holds). When the venue's stream folded a fill or a cancel BEFORE its acknowledgement reached us (a marketable order, an IOC), the 150=F or 150=4 arrives first and the late 150=0 restates the record truthfully: 39 is then 1, 2, 4 or 6, 14 the filled quantity, 151 what is left and 9113 the state; a 150=0 never regresses an order to New (4.2).35=8 150=8 39=8 with our OrderID(37) (the row exists, state REJECTED), the sentence in Text(58), 9110=4000 and the venue's own code in 9114 VenueRejectCode, from the order.rejected site.Recon:IntervalSeconds (300 by default) and the 150=0 or 150=8 then arrives from the same sites; a 35=H meanwhile reads 39=A 9113=SENT. Never re-enter the order under a new reference before that: read GET .../orders/open.The session itself builds only the refusal of 3.4 (nothing was written), the restatement of a replay (3.3) and the answers of a 35=H (3.7).
ClOrdID(11): unique per client for 24 hoursThe reference has the Idempotency-Key semantics of docs/11 section 5, on the same store: the reference is CLAIMED before the order is built and the outcome is kept for 24 hours under the client it named.
55, 54, 38, 40, 44, 59, 18, 99, 9101, 9102, 9103, 9104 and the same client): nothing is placed; the ORIGINAL order is restated as 35=8 150=I with 9117 Duplicate=Y, its real OrderID(37), 9110=4092 and its state at that moment (a terminal one when it has since ended: a restatement, never a lifecycle event). When the first use was a refusal before the venue (no order exists), the same 150=8 is answered again with 9117=Y.35=8 150=8 39=8 103=6 (duplicate order) Text(58) ClOrdID reused with a different payload 9110=4092, nothing placed.35=8 150=8 39=8 103=6 Text(58) A request with this ClOrdID is still in progress. 9110=4092, nothing placed. Read the state back before retrying.The reference is held for 24 hours and never released early: run one reference per order attempt and never reuse a refused one for a corrected order. Only a failure of ours BEFORE the venue call gives the reference back (the session answers 35=j 380=4, 8.3; nothing reached the venue). Once the order reached the venue the reference is kept whatever fails on our side afterwards (revision 11, docs/11 revision 19): the order stands as sent, nothing is answered on the session (3.2) and a reuse with the IDENTICAL payload restates it as 150=I 9117=Y; it never places a second order. OrigClOrdID(41) on a 35=F or 35=H resolves through the same store when it holds the outcome of the reference, and through the order record itself (the newest order of that client placed over FIX under the reference) whenever the store has no completed outcome for it: once the store has forgotten the reference (after the 24 hours), and while the reference's first use is still in flight. That last arm matters to a partner running more than one session: the 150=0 of an order leaves before the store records the outcome, so a 35=F or 35=H naming the order by OrigClOrdID(41) from ANY session of yours (a risk desk cancelling on the first 150=0 of its drop copy) resolves it as soon as its acknowledgement has been reported, the reference in flight or not. A reference whose first use has not produced an order yet (before our record exists, or refused before the venue) answers as unknown: 102=1 on a 35=F, 103=5 on a 35=H, 9110=4004. A resting GTC order therefore stays cancellable and readable by your own reference for its whole life, and a partner that never reads our order ids can run an entire lifecycle on its own references. After the 24 hours a reference may be reused for a NEW order (the store no longer holds it): 41 then names the newest one, and never the earlier order while the new one is still being placed.
A reference is stored as sent and never trimmed, and one that begins or ends with whitespace never reaches the store: a ClOrdID(11) of that shape is refused at the door of 3.1, and an OrigClOrdID(41) of that shape is refused at the door BEFORE it is resolved, 35=9 102=99 9110=4000 on a 35=F and 35=j 380=0 9110=4000 on a 35=H, both with Text(58) OrigClOrdID(41) begins or ends with whitespace and is refused (revision 9). A padded twin of a live reference of yours (ABC beside ABC) therefore places nothing, replays nothing and cancels nothing: it is refused, not read as the reference without the blank.
ExecutionReport 150=8A refusal the router returns before anything is written answers a gateway-built 35=8 150=8 39=8 OrderID(37)=0 (nothing exists), your 11, a gateway ExecID(17), 55, 54, 38, 40 and 59 echoed (59=1 when you sent none), 9102 as applied, 151=0, 14=0, 6=0, the Parties component echoed, Text(58) = the router's sentence VERBATIM, 9110 = our code, 9113=REJECTED, 9121=FIX, 60, and OrdRejReason(103) classified as below. The reference is recorded (3.3).
| Case | 9110 | 103 |
|---|---|---|
The verification gate (the client's level does not allow the market; Text(58) names the sections to complete and 9118 MissingBlocks lists them, comma joined) | 4030 | 15 (unknown account: the account is not yet allowed) |
The TERMS re-consent gate (revision 6, docs/11 section 10.5: the client has to accept the version in force of a gated consent document before opening a new position; Text(58) is the sentence naming where to accept, no 9118; record the acceptance with POST .../consents; never on a reduce-only or reducing order, never while our switch is off) | 4035 | 99 (other) |
The partner's clients are close-only under the settlement tier (revision 6, docs/11 15.7: an item unsettled past the escalation age or an unsettled net above the agreed cap; lifted on our hourly check): the sentence in Text(58), no 9118; never on a reduce-only or reducing order | 4041 | 99 (other) |
Your server-side order entry is CLOSED (revision 12, docs/11 section 21 and 22.10: your clients' orders, cancels and leverage changes are entered through the app order commands alone, a switch our operations set only on our compliance function's decision and announce at least 30 days ahead): Text(58) Server-side order entry is closed for this partner: orders, cancels and leverage changes are entered through the app order commands., refused after the whitespace door and before the rate limit and the store of 3.3, so the reference is NOT recorded and runs as a first use once the switch is back | 4043 | 99 (other) |
This market is not offered., the barrier-by-RFQ sentence, a family not tradable yet | 4000 | 1 (unknown symbol) |
The quantity sentences (The quantity must be a number greater than zero., The quantity must be a multiple of the lot size ...) | 4000 | 13 (incorrect quantity) |
| The shape sentences (the side, the type, the time in force, post-only, the price and trigger sentences of 3.1, the spot refusals) | 4000 | 11 (unsupported order characteristic) |
The conditional limit band (revision 15, docs/11 12.3 revision 26): a 40=4 whose Price(44) lies outside the market's band (priceBandBps on GET /market-info) around its StopPx(99), or around the mark once the mark has reached the StopPx(99), because the venue checks neither the trigger nor the limit of a conditional order; Text(58) for example The limit price 1 is outside the price band of this market: this order becomes a limit order at its trigger price 59000, so its limit price must lie within 10 percent of the trigger price, from 53100 to 64900.; reduce-only orders too; on a market that publishes a band, without a fresh mark the mark sentence of the next row | 4000 | 99 (other) |
Every other sentence (Your account is not active for trading yet., a restriction or halt, a disabled market, the risk engine unavailable, the margin admission, the price grid, There is no fresh mark price for ...) | 4000 | 99 (other) |
The rate limit of section 1 (the shared trade_per_minute bucket) | 4290 | 99 |
A ClOrdID reuse (3.3) | 4092 | 6 |
103 is a coarse classification by sentence: key your handling on 9110 and Text(58), never on 103 alone (the venue's own guidance for its 103=99). A refusal by the VENUE is not gateway-built: it arrives from the hook site with the row's real OrderID(37) (3.2). Exactly one of the two shapes ever answers a given 35=D.
8=FIX.4.4|9=149|35=D|49=SWISSBORG|56=BARRIERS|34=3|52=20260908-09:00:00.300|11=ord-1002|453=1|448=cust-001|447=D|452=3|55=BTC-USD-PERP|54=1|38=0.00015|40=2|44=60000|10=223|
8=FIX.4.4|9=330|35=8|49=BARRIERS|56=SWISSBORG|34=3|52=20260908-09:00:01.000|37=0|11=ord-1002|17=rej-1788483600123-4|150=8|39=8|55=BTC-USD-PERP|54=1|151=0|14=0|6=0|453=1|448=cust-001|447=D|452=3|103=13|9110=4000|58=The quantity must be a multiple of the lot size 0.0001.|38=0.00015|40=2|59=1|9102=N|9113=REJECTED|9121=FIX|60=20260908-09:00:01.000|10=032|
| Tag | Required | Meaning |
|---|---|---|
ClOrdID(11) | optional | The cancel's own reference, echoed on a 35=9; not stored (a cancel is not idempotency-keyed: a second cancel of an order already being cancelled is silently accepted, so a retry is harmless). |
OrigClOrdID(41) | one of 41, 37 | The ClOrdID(11) of the order, resolved through the store of 3.3 for the client named in Parties and, whenever the store has no completed outcome for it (past the store's 24 hours, or while the reference's first use is still in flight), through the order record (the newest FIX order of the client under the reference, from the moment the record exists). A value that begins or ends with whitespace, or is whitespace only, is refused at the door before it is resolved (the table below, revision 9). |
OrderID(37) | one of 41, 37 | Our order id (4185, or BARR-4185). When both are present, 41 is used. |
Parties | required | Section 2. The order must belong to that client, else it is unknown. |
Symbol(55), Side(54) | optional | Accepted and ignored (FIX 4.4 marks them required on a 35=F; the router identifies the order by 41 or 37 alone). |
An accepted cancel answers NOTHING on the session: the venue confirms it and the 150=4 (or 150=C) arrives from the order.canceled site when the venue's frame lands; until then a 35=H reads 39=6 9113=PENDING_CANCEL. A cancel of an order already being cancelled is the same silence (its report is already on its way). The order need not have been placed on your session's key: a trading session may cancel any order of the client it names (one placed over REST, on another key of yours, or by our risk engine), and the key that asked for the cancel then receives the order's reports from the cancel on (the 150=4 or 150=C, or the fill that beat the cancel) beside the key that placed it and the drop copies, so a cancel never appears to hang (section 5). A refusal answers OrderCancelReject(35=9):
| Case | 434 | 102 | Text(58) | 9110 |
|---|---|---|---|---|
Neither 41 nor 37 (absent or empty) | 1 | 1 | OrderCancelRequest requires OrigClOrdID(41) or OrderID(37) | none |
41 begins or ends with whitespace, or is whitespace only (revision 9: refused at the door before the store or the record is read, whether or not 37 is present; 37 as you sent it or 0, 41 echoed as sent) | 1 | 99 | OrigClOrdID(41) begins or ends with whitespace and is refused | 4000 |
41 names no order of this client (never used, refused before the venue, or its first use has not produced the order yet: unknown to the store and to the order record), or 37 not this client's | 1 | 1 | This order was not found. | 4004 |
The order has ended (This order has already been filled., This order has already been cancelled., This order was rejected, there is nothing to cancel.) | 1 | 0 (too late to cancel) | the sentence | 4000 |
| The order never reached the venue (a row still CHECKED) | 1 | 99 | This order has not reached the trading venue yet. Please try again in a moment. | 4000 |
| The venue refused the cancel | 1 | 99 | the sentence, 9114 = the venue's code | 4000 |
| Your server-side order entry is CLOSED (revision 12, docs/11 22.10), checked after the whitespace door and before the reference is resolved | 1 | 99 | Server-side order entry is closed for this partner: orders, cancels and leverage changes are entered through the app order commands. | 4043 |
| The rate limit | 1 | 99 | Too many requests. Please slow down and try again. | 4290 |
A 35=9 carries OrderID(37), ClOrdID(11) and OrigClOrdID(41) as FIX 4.4 requires: 37 is the order the request resolved to when the router knows it (a cancel by 41 alone), else what you sent; a reference you did not send, or sent EMPTY, is reported as the sentinel BARR-NONE for 11 and 41 and 0 for 37 (the BARR- namespace is refused on order entry, so the sentinel can never collide with a reference of yours). 39=8 and the Parties component ride on every 35=9. A 35=9 always means the request was REFUSED and the order is unchanged.
A cancel the venue answered that it could not confirm (revision 13: its code 5000, or a 5xx without a venue code) is NOT refused: the cancel may have taken effect. The session answers the reply policy's 35=j 380=4 9110=5030 (8.3), never a 35=9; the order is unchanged on our side and your session stays recorded as the cancelling key, so the 150=4 arrives from the hook site when the venue's frame lands. A cancel may be sent again at once (a 35=9 to it then means the order had already ended), or read the order back with a 35=H.
8=FIX.4.4|9=112|35=F|49=SWISSBORG|56=BARRIERS|34=4|52=20260908-09:00:00.400|11=cxl-1|41=ord-1001|453=1|448=cust-001|447=D|452=3|10=221|
(nothing: the venue's frame produces the report below)
8=FIX.4.4|9=312|35=8|49=BARRIERS|56=SWISSBORG|34=5|52=20260908-09:00:00.500|37=4185|11=ord-1001|41=ord-1001|17=can-1788483600200-6|150=4|39=4|55=BTC-USD-PERP|54=1|38=0.01|40=2|44=60000|59=1|14=0|151=0|6=0|58=canceled|9102=N|9113=CANCELED|9115=order:4185:canceled|9121=FIX|60=20260908-09:00:00.500|453=1|448=cust-001|447=D|452=3|10=154|
No amend route exists on the Partner API; every OrderCancelReplaceRequest answers OrderCancelReject(35=9) with CxlRejResponseTo(434)=2, CxlRejReason(102)=99, OrdStatus(39)=8, Text(58) Cancel/Replace is not supported: cancel and re-enter., the sentinels of 3.5 for the references it carried, and nothing else happens. Cancel the order and enter a new one under a new ClOrdID(11).
8=FIX.4.4|9=133|35=G|49=SWISSBORG|56=BARRIERS|34=7|52=20260908-09:00:00.700|37=4185|11=new-ref|41=old-ref|55=BTC-USD-PERP|54=1|38=0.02|40=2|44=59000|10=009|
8=FIX.4.4|9=165|35=9|49=BARRIERS|56=SWISSBORG|34=7|52=20260908-09:00:00.700|37=4185|11=new-ref|41=old-ref|39=8|434=2|102=99|58=Cancel/Replace is not supported: cancel and re-enter.|10=075|
OrigClOrdID(41) or OrderID(37) plus Parties, resolved as in 3.5 (a reference whose first use is in flight restates the row as soon as it exists; one that has not produced an order yet answers the unknown-order shape below); send Symbol(55) and Side(54) as FIX 4.4 requires (the unknown-order answer echoes them). The answer is ONE ExecutionReport(35=8) 150=I restating the order from its record: 37, 11 (your reference when the order was placed over FIX, absent otherwise), 41 echoed when you sent it, 39 per 4.2, 55, 54, 38, 40, 44, 99, 59, 14 = the filled quantity, 151 = the unfilled quantity on a live state and 0 on a terminal one, 6 = the average fill price, 9113 = the router's own state verbatim, 9121, the Parties component, 60, and, on a REJECTED order, 58 = the reject sentence. It is a STATUS report, never a lifecycle event (4.3). A 35=H is accepted on a drop-copy session and counts against your read_per_minute bucket.
An order the client does not hold answers 35=8 150=I 39=8 OrdRejReason(103)=5 (unknown order) 37=0 Text(58) This order was not found. 9110=4004, with 55 and 54 echoed when the request carried them and omitted otherwise (FIX 4.4's own convention for an unknown order on a status request), and without 38, 40, 59, 9102, 9113 and 9121 (no order exists to read them from: the one exception to the always-on set of 4.1, whose presence cells name it). A 35=H with neither 41 nor 37 is refused by name: Reject(35=3) 373=1 371=37 Text(58) OrderStatusRequest requires OrigClOrdID(41) or OrderID(37). An OrigClOrdID(41) that begins or ends with whitespace, or is whitespace only, is refused at the door before it is resolved, whether or not 37 is present: BusinessMessageReject(35=j) 380=0 379 = the value as sent 9110=4000 Text(58) OrigClOrdID(41) begins or ends with whitespace and is refused (revision 9), never the 150=I of the reference without the blank.
8=FIX.4.4|9=124|35=H|49=SWISSBORG|56=BARRIERS|34=8|52=20260908-09:00:00.800|41=ord-1001|55=BTC-USD-PERP|54=1|453=1|448=cust-001|447=D|452=3|10=123|
8=FIX.4.4|9=293|35=8|49=BARRIERS|56=SWISSBORG|34=9|52=20260908-09:00:00.900|37=4185|11=ord-1001|41=ord-1001|17=sta-1788483600300-7|150=I|39=1|55=BTC-USD-PERP|54=1|38=0.01|40=2|44=60000|59=1|14=0.004|151=0.006|6=60000.000000|9102=N|9113=PARTIAL|9121=FIX|60=20260908-09:00:00.900|453=1|448=cust-001|447=D|452=3|10=215|
One report per state change of an order, built at the SAME hook sites as the order.* webhooks of docs/11 section 16.4 (the acknowledgement in the routing pipeline, the fills and the frames in the venue ingest, the resolutions of our reconciliation), so REST, the webhooks and FIX never disagree about an order. A report is built inside the router's transaction and queued to your session after the commit; a rolled-back fold never reaches you. 9115 SourceRef carries the webhook's own source_ref (order:4185:accepted, order:4185:fill:88002, order:4185:canceled, order:4185:rejected): a partner running both transports dedupes on it.
One row per tag, in the order the router's registry keeps them; the Presence column is the registry's own rule, generated from the code.
| Tag | Presence | Content |
|---|---|---|
OrderID(37) | always | Our order id (4185; the orderId of GET .../orders); 0 on a gateway-built refusal with no row (3.4) and on the unknown-order status (3.7). |
ClOrdID(11) | when known | Your ClOrdID(11) (the record's channelRef) on an order placed on a FIX session; ABSENT, never empty, on an order placed over REST, by an app order command (revision 12: its channelRef is the token's jti, our record of the command, never printed as a ClOrdID) or by our risk engine (a drop-copy session reads 37). |
OrigClOrdID(41) | on 150=4, 150=C and a restatement answering a 35=H that carried 41 | The order's own FIX reference (on a cancel and an expiry of an order placed on a FIX session, so a partner correlating by 41 finds its order), or the 41 you sent. |
ExecID(17) | always | Unique within the session, gateway-built (acc-, fil-, can-, exp-, rej-, sta-, dup- followed by a clock reading and a counter). NOT a cursor and not stable across sessions: dedupe durable facts on 9115 and, for fills, on 9111. |
ExecType(150) | always | 0 acknowledged, F trade, 4 canceled, C expired, 8 rejected, I order status (a restatement). |
OrdStatus(39) | always | 0 new, 1 partially filled, 2 filled, 4 canceled, C expired, 8 rejected, A pending new (a restatement of a row not yet acknowledged), 6 pending cancel (a restatement of a row being cancelled). 4.2. |
Symbol(55) | always; on the unknown-order status of 3.7 echoed when the 35=H carried it | The market. |
Side(54) | always; on the unknown-order status of 3.7 echoed when the 35=H carried it | 1 / 2. |
OrderQty(38) | always, except on the unknown-order status of 3.7 | The order's quantity (decimal string). |
OrdType(40) | always, except on the unknown-order status of 3.7 | 1, 2, 3, 4 from the record's type (a TAKE_PROFIT type reads 3 or 4 with 9101=TAKE_PROFIT). |
Price(44) | when the order has one | The limit price (LIMIT), or the limit price of a limit-triggered order. |
StopPx(99) | on conditional orders | The trigger price. |
TimeInForce(59) | always, except on the unknown-order status of 3.7 | 1, 3, 4; a POST_ONLY order reads 59=1 with 18=6. |
ExecInst(18) | on post-only | 6. |
LastQty(32) | on 150=F | This fill's quantity. |
LastPx(31) | on 150=F | This fill's price. |
CumQty(14) | always | The filled quantity after this report (the record's filledQty, its own numeric text). |
LeavesQty(151) | always | The unfilled quantity on a live state, 0 on a terminal one (a withdrawn remainder is not leaves). |
AvgPx(6) | always | The volume-weighted average price of the order's fills to 6 places (sum(price x qty) / sum(qty)), 0 before the first fill. |
Commission(12) | on 150=F | This fill's commission in USDC to 6 places. |
CommType(13) | on 150=F | 3 (absolute). |
Currency(15) | on 150=F | USDC. |
TransactTime(60) | always | The fill's venue timestamp on 150=F, the state change's time otherwise, yyyyMMdd-HH:mm:ss.fff UTC. |
Text(58) | on 150=8, 150=4, 150=C and a restatement with a reason | The reject sentence, canceled or expired (the webhook's reason, verbatim), the sentence of a REJECTED order restated. |
OrdRejReason(103) | on 150=8 and the unknown-order 150=I | 3.4; 5 on the unknown-order status. Absent on a venue refusal (key on 9110, 9114 and 58). |
NoPartyIDs(453) | always | 1. |
PartyID(448) | always | The client: on a hook-built report the client's partnerClientRef when set, else our uid, on every session that receives it; a gateway-built report (3.4, 3.7, a replay) echoes the value you sent. |
PartyIDSource(447) | always | D. |
PartyRole(452) | always | 3. |
9101 TriggerKind | on conditional orders | STOP or TAKE_PROFIT. |
9102 ReduceOnly | always, except on the unknown-order status of 3.7 | Y or N (the record's reduceOnly). |
9103 TriggerSource | on conditional orders | From the record's trigger (LAST, MARK, INDEX). |
9104 TriggerDirection | on conditional orders | From the record's trigger (AT_OR_ABOVE, AT_OR_BELOW). |
9110 Code | on 150=8, on a refused status, on a replay | Our code (4000, 4004, 4030, 4035, 4041, 4043, 4092, 4290). |
9111 TradeId | on 150=F | The venue trade id, the same id the webhook's fill.tradeId and GET .../trades carry. |
9112 RealizedPnl | on 150=F | The fill's realised P&L in USDC, signed. |
9113 RouterState | always, except on the unknown-order status of 3.7 | The record's state verbatim (CHECKED, SENT, ACKED, PENDING_TRIGGER, PARTIAL, PENDING_CANCEL, FILLED, CANCELED, REJECTED): what 39 collapses, you can still read. |
9114 VenueRejectCode | on a venue refusal | The venue's numeric code (the webhook's rejectCode). |
9115 SourceRef | on every hook-built report | The event's source_ref verbatim: the dedupe key across FIX and the webhooks. Absent on a gateway-built report (3.4, 3.7, a replay). |
9116 FillRole | on 150=F when known | MAKER or TAKER. |
9117 Duplicate | on a replay | Y (3.3). |
9118 MissingBlocks | on a 4030 refusal | The verification gate's missingBlocks, comma joined. |
9121 Origin | always, except on the unknown-order status of 3.7 | FIX (placed on a FIX session of yours), REST (placed over the Partner API), APP (placed by an app order command your app sent us directly with the token you signed, revision 12, docs/11 section 22; no 11, no derived 41), CLOSE_OUT (an order of our risk engine: a flatten, or the forced sale of a client's spot holdings after a close-out, docs/13 section 8). |
The tags the router commits to on EVERY 35=8 (the positive half of our tag-integrity pin): 37, 17, 150, 39, 55, 54, 38, 40, 59, 151, 14, 6, 60, 453, 448, 447, 452, 9102, 9113, 9121; the unknown-order status of 3.7 alone carries neither 38, 40, 59, 9102, 9113 nor 9121 (no order exists to read them from), and 55 and 54 on it are echoes. No tag is ever sent with an empty value: what is not known is omitted.
ExecType and OrdStatus per transitionTransition (the record's state after) | Webhook | 150 | 39 | Notes |
|---|---|---|---|---|
| The venue acknowledged (ACKED) | order.accepted | 0 | 0 | 151 = the quantity. When a fill or a cancel was folded BEFORE the acknowledgement landed, 39 is the record's status instead (1, 2, 4, 6), 14 and 151 the figures after the fold, 9113 the state: the earlier 150=F or 150=4 stands and the 150=0 restates it, never regresses it (3.2). |
| The venue holds a conditional order (PENDING_TRIGGER) | order.accepted | 0 | 0 | 9113=PENDING_TRIGGER (FIX has no distinct status). |
| The conditional order triggers (PENDING_TRIGGER to ACKED) | none | No report (the same fact); a 35=H reads 9113=ACKED. | ||
| A fill that leaves quantity (PARTIAL) | order.partially_filled | F | 1 | 32, 31, 14, 151, 6, 12, 9111, 9112, 9116. |
| A fill that completes the quantity (FILLED) | order.filled | F | 2 | 151=0. |
| A fill on an order already ended or being cancelled (the venue filled before the cancel took) | order.partially_filled | F | the record's status: 4 on CANCELED, 6 on PENDING_CANCEL, 8 on REJECTED | Unusual FIX, reported truthfully; 9113 names the state and 14, 6 advance. |
| The venue canceled (CANCELED), or our reconciliation found it closed | order.canceled, reason canceled | 4 | 4 | 151=0, 58=canceled, 41 = your reference. |
| The venue expired it (the record stores CANCELED) | order.canceled, reason expired | C | C | 58=expired. A later restatement of the same record reads 39=4 (9113=CANCELED). |
| The venue refused (the acknowledgement, a later frame, or "venue never received" from our reconciliation) | order.rejected | 8 | 8 | 58 = the sentence, 9110=4000, 9114 when the venue gave a code. |
| Refused before the venue (no record) | none (gateway-built, 3.4) | 8 | 8 | 37=0. |
A 35=H, or a ClOrdID replay | none (gateway-built, 3.7, 3.3) | I | A for CHECKED and SENT, 0 for ACKED and PENDING_TRIGGER, 1 PARTIAL, 6 PENDING_CANCEL, 2 FILLED, 4 CANCELED, 8 REJECTED | Never a lifecycle event. |
| A flatten of our risk engine (every transition above) | the same events | the same | the same | Drop-copy sessions only, 9121=CLOSE_OUT. |
| An order placed by an app order command (revision 12, docs/11 section 22; every transition above) | the same events | the same | the same | Drop-copy sessions only (and a session whose key cancels it, section 5), 9121=APP, no 11 and no derived 41. |
Exactly one report with a terminal 39 and a 150 other than I ever closes a given order; a 150=I may carry a terminal 39 after it.
150=I ruleA restatement (a 35=H answer, a ClOrdID replay) is a STATUS report: it carries a gateway ExecID(17), no 9115, reports the order's state at the instant it is built, and never consumes the order's single terminal lifecycle report. Treat a 150=I as a snapshot to reconcile against, never as a transition to act on.
9115 SourceRef on every hook-built report is the same source_ref the webhook delivery carries, and 9111 TradeId on a fill is the same trade id fill.tradeId and GET .../trades carry. A partner running FIX beside the webhooks dedupes on those two; a partner running FIX alone still dedupes on 9115, because our emit is at-least-once per fact (section 6).
When the venue's answer to a placement is lost in transit, or the venue answered that it could not confirm it (revision 13), the session receives nothing (3.2). Our reconciliation resolves the row within Recon:IntervalSeconds (300 by default): the 150=0 or 150=8 then arrives from the same hook sites with the same 9115, before any fill of that order. A 35=H meanwhile reads 39=A 9113=SENT. Never re-enter the order under a new reference before the resolution: read GET .../orders/open.
8=FIX.4.4|9=288|35=8|49=BARRIERS|56=SWISSBORG|34=4|52=20260908-09:00:00.100|37=4185|11=ord-1001|17=acc-1788483600100-2|150=0|39=0|55=BTC-USD-PERP|54=1|38=0.01|40=2|44=60000|59=1|14=0|151=0.01|6=0|9102=N|9113=ACKED|9115=order:4185:accepted|9121=FIX|60=20260908-09:00:00.100|453=1|448=cust-001|447=D|452=3|10=150|
8=FIX.4.4|9=387|35=8|49=BARRIERS|56=SWISSBORG|34=5|52=20260908-09:00:05.100|37=4185|11=ord-1001|17=fil-1788483605100-3|150=F|39=1|55=BTC-USD-PERP|54=1|38=0.01|40=2|44=60000|59=1|32=0.004|31=60000|14=0.004|151=0.006|6=60000.000000|12=0.120000|13=3|15=USDC|9102=N|9111=88002|9112=0.000000|9113=PARTIAL|9115=order:4185:fill:88002|9116=TAKER|9121=FIX|60=20260908-09:00:05.100|453=1|448=cust-001|447=D|452=3|10=078|
8=FIX.4.4|9=382|35=8|49=BARRIERS|56=SWISSBORG|34=6|52=20260908-09:00:06.100|37=4185|11=ord-1001|17=fil-1788483606100-4|150=F|39=2|55=BTC-USD-PERP|54=1|38=0.01|40=2|44=60000|59=1|32=0.006|31=60100|14=0.010|151=0|6=60060.000000|12=0.180000|13=3|15=USDC|9102=N|9111=88003|9112=0.000000|9113=FILLED|9115=order:4185:fill:88003|9116=TAKER|9121=FIX|60=20260908-09:00:06.100|453=1|448=cust-001|447=D|452=3|10=060|
A drop-copy session is a session that receives every report of every client of the partner (REST-placed orders, orders of other FIX sessions, the orders your app placed by app order commands, the flattens of our risk engine, the quote and contract reports of every request and contract, section 7) and may not enter orders or requests. It is established at Logon with 9120=Y on a TRADE key of its own (one key, one session: section 1); the Logon reply echoes 9120=Y. On a drop-copy session a NewOrderSingle(35=D), OrderCancelRequest(35=F), OrderCancelReplaceRequest(35=G), QuoteRequest(35=R), QuoteResponse(35=AJ) or QuoteCancel(35=Z) is refused at the session level with Reject(35=3) SessionRejectReason(373)=99, RefMsgType(372) = the type and Text(58) drop-copy session: order entry is disabled, use a trading session; an OrderStatusRequest(35=H) is a read and is served. Run it with its own key so the trading and observing entitlements are separable; a drop-copy session shares the partner's rate limits.
The routing of a report is keyed on the KEY of the session:
channelKeyId is its key: what was placed ON that key, including after a reconnect or a replacement under the same key (section 1). An order placed before a disconnect therefore reports to the session that reconnects with the same key.9121=APP and without ClOrdID(11) or a derived OrigClOrdID(41): its channelRef is the token's jti, our record of the command, never a reference of yours. A 35=H naming it by OrderID(37) is served; it has no 41 to name it by.9121 Origin marks every report: FIX (placed on a FIX session of yours), REST (placed over the Partner API), APP (placed by an app order command), CLOSE_OUT (an order of our risk engine: a flatten of a perpetual position, or the forced sale of a client's spot holdings after a close-out left its cash negative, a SELL MARKET IOC on the spot market in the client's name, docs/13 section 8; it runs for a client only once that client has accepted the Terms version carrying the right-of-sale clause, docs/11 section 12.3).150=4 or 150=C, or a 150=F for a fill that beat the cancel, so the outcome of every cancel reaches the session that asked for it (3.5). The cancel of another key's order by its OrigClOrdID(41) works from the acknowledgement on: a risk desk that cancels on the first 150=0 of its drop copy is honoured even while the placing session's reference is still being recorded (3.3). A cancel over REST changes nothing here.PartyID(448) is the client's partnerClientRef when set, else our uid, on every session that receives it: the hook site does not see the message that placed the order, so the trading session is not echoed the exact value it sent (a gateway-built report is).A partner wanting one feed of everything runs a drop-copy session on a second key beside its trading sessions; a risk desk that only observes runs the drop-copy key alone.
Heartbeat answering your TestRequest may take a MsgSeqNum between two reports (it is sent from the read loop). Nothing is persisted for FIX: no resend store, no replay. Your session receives reports from the Logon reply on: the 35=A is always the first frame of the session, with MsgSeqNum(34)=1, and a report built while your Logon is still being answered (our sessions register is being written) is lost for that session like one built with no live session.GET /partner/v1/clients/{clientId}/orders/open, GET .../orders, GET .../rfq and GET .../barriers, and through the webhooks, which keep every event with retries; a 35=H per open order after the reconnect is the FIX-only way to the same facts (its 150=I folds the fills you missed into 14 and 6).Logout slow consumer and the close. Reports are never dropped to save a laggard; there is no best-effort class on this gateway.source_ref (9115 on FIX) and the same trade id, so a partner running both dedupes on those.source_ref) deliveries and skips a repeat within that window (the message type is part of the key, so the 35=AI and the 35=8 of one accepted request, which share one source_ref, both pass); after a router restart the memory is empty, so a fact re-emitted by our reconciliation can reach a session once more. Dedupe on 9115.The barrier lifecycle of docs/11 section 12.2 in FIX terms, on exactly the same router functions, gates and sentences as POST .../rfq, POST .../rfq/{rfqId}/accept, POST .../rfq/{rfqId}/cancel and POST .../barriers/{contractId}/close: whatever FIX answers about a request, REST and the rfq.* / barrier.closed webhooks answer about the same fact. Barrier markets are RFQ only: a 35=D on a KO symbol answers 150=8 with Barrier contracts are traded by request for quote, not by order: request quotes on the Barriers page (/barriers), or through POST /v1/rfq. (103=1), and a 35=R on a PERP or SPOT symbol answers 35=AI 297=5 Requests for quote are available on barrier markets only..
The messages:
| Direction | Message | Meaning |
|---|---|---|
| you to us | QuoteRequest(35=R) | An opening request with the barrier terms (7.1), or, with 9155 CloseContractId, an early close request of one of the client's open contracts. |
| us to you | QuoteStatusReport(35=AI) | The request's state, at creation and every time it changes (7.2): active, pending, accepted, expired, canceled, rejected. |
| us to you | Quote(35=S) | One live quote of the request, once per quote id (7.3). |
| you to us | QuoteResponse(35=AJ) | Accept a quote (7.4). |
| you to us | QuoteCancel(35=Z) | Withdraw the request (7.5). |
| us to you | ExecutionReport(35=8) on the KO symbol | The contract open, or the early-close trade (7.6), and the contract end for any reason (7.7). |
The gateway itself builds only the creation-time 35=AI (297=16, or 297=10 when the venue could not be reached or, since revision 13, answered that it could not confirm the request, or 297=5 when the request was refused before a row existed), the replays of a reused reference and the refusals of an accept or a cancel (35=j 380=0). Every state change of a request and every contract report comes from the SAME hook sites as the rfq.quote, rfq.accepted, rfq.expired, rfq.canceled, rfq.rejected and barrier.closed webhooks (7.8), with the event's source_ref in 9115. A request made over FIX reads channel: "FIX", channelKeyId and channelRef (= your QuoteReqID(131)) on the records of docs/11 12.2.
| Tag | Required | Type | Meaning |
|---|---|---|---|
QuoteReqID(131) | named on absence (Reject(35=3) 371=131) | String, 1 to 64 characters, never starting with BARR- | Your reference for the request, unique per CLIENT for 24 hours: the request's Idempotency-Key (7.1.1). It rides on every report of the request as 131, and on the contract reports as ClOrdID(11). A reference of the wrong shape answers 35=AI 297=5 QuoteReqID(131) must be 1 to 64 characters and must not start with BARR-. A reference that begins or ends with whitespace, or is whitespace only, is refused at the door the same way (297=5 9110=4000, no 9140, the value echoed as sent, an opening and a close request alike) with Text(58) QuoteReqID(131) begins or ends with whitespace and is refused, before the store of 7.1.1 is read or written and before a close request's contract is looked up: nothing is trimmed, so ABC is never read as ABC (revision 9). |
Parties (453, 448, 447, 452) | required (35=j) | section 2 | The client. |
NoRelatedSym(146) | default on absence | int | Must be 1 (one symbol per request). Any other value: 297=5 one symbol per request (NoRelatedSym(146) must be 1). |
Symbol(55) | named on absence (opening) | String | A KO market (BTC-USD-KO). On a close request (9155 present) it is optional and, when present, must be the contract's market (297=5 Symbol(55) does not match the contract's market). |
Side(54) | named on absence (opening) | Char | 1 BUY (long, knock-out below the entry), 2 SELL (short, knock-out above). A value outside the FIX 4.4 enumeration answers Reject(35=3) 373=5 (an echoed 54 would make the 35=AI invalid FIX); a value inside it the router does not trade keeps the module's sentence The side must be BUY (long, knock-out below the entry) or SELL (short, knock-out above the entry).. Ignored on a close request (the closing side is derived). |
OrderQty(38) | named on absence (opening) | Qty | The quantity, on the market's lot grid as GET /market-info publishes it (lotSize; fractional on the knock-out markets since the venue lists them on the perpetual's grid, 0.0001 on BTC-USD-KO, docs/11 revision 12; 1 stays valid on every grid; an off-grid value answers 297=5 with the lot sentence), at least the market's minQty (The quantity must be at least 0.0001.) and worth at least the market's minNotional at the current mark (The request value is below the minimum for this market (100.00 USD)., 9110=4000, refused before a row: no 9140, no rfq.rejected webhook; exactly at the minimum passes). Echoed unchanged on the reports. Ignored on a close request (the contract's quantity). |
9130 BarrierLevel | named on absence (opening) | Price | The knock-out price. |
9131 TakeProfit | optional | Price | The take profit, a price on the profit side of the entry; omit for a knock-out-only contract. |
9132 StrictTakeProfit | default on absence | Char | Y = strict take profit (pays only on a touch; needs 9131, an explicit 9133 at most 8 h ahead (the binding cap, Strict take profit needs an expiration at most 8 hours ahead.; the venue's 24 h ceiling is the outer bound) and a market that offers it: the module's three sentences apply; every client, whatever its categorisation, also needs the current Risk Disclosure accepted (the strict consent gate, 297=5 9110=4035 with Accept the current Risk Disclosure before requesting a strict take profit contract. in Text(58)), and a RETAIL client additionally the acknowledged current strict key information document KID_<SYMBOL>_STRICT (the KID gate, the same 9110=4035 with its own sentence; PROFESSIONAL and ECP clients are outside the KID gate); both acknowledgements are recorded over REST, POST .../consents), N or absent = not. Any other value: 297=5 StrictTakeProfit(9132) must be Y or N. |
9133 ExpiryTs | optional | int, Unix ms | The contract expiry, at most 8 hours after our clock at receipt (The expiry can be at most 8 hours ahead, or leave it out for an open-end contract.); omitted = the open-end contract (12 calendar months, capped at the venue's published maximum contract life less 60 s, 9149=Y; the facts on GET /market-info as barrierExpiry). Unix milliseconds, the REST body's own unit, so both transports carry one number. An unreadable value: 297=5 ExpiryTs(9133) must be a Unix millisecond timestamp. |
9134 AutoAccept | default on absence | Char | Y (default) = the router accepts the first acceptable live quote itself; N = the manual flow (35=S then 35=AJ). Any other value: 297=5 AutoAccept(9134) must be Y or N. |
9135 PremiumBudget | optional | Amount | The premium budget every quote is measured against (the REST body's premium). |
9136 ExpiresInMs | optional | int | The quoting window in milliseconds, default 60000, 1 s to 24 h (The quoting window must be between 1 second and 24 hours.). An unreadable value: 297=5 ExpiresInMs(9136) must be a number of milliseconds. |
ExpireTime(62) | optional | UTCTimestamp | The quoting window's end: the window is the time left to it. When both 62 and 9136 are present 9136 wins and 62 is ignored; an instant in the past yields the window sentence. An unreadable value: 297=5 ExpireTime(62) must be a UTCTimestamp (yyyyMMdd-HH:mm:ss or yyyyMMdd-HH:mm:ss.sss). |
9155 CloseContractId | optional | int | Present = an EARLY CLOSE request of that contract (docs/11 12.2, the close by RFQ): the contract must be the client's and OPEN (This contract was not found. with 9110=4004, This contract is no longer open.), no other close request may be open on it (A close request for this contract is already open. Accept one of its quotes or cancel it first.); 55 must be the contract's market when present (the Symbol row above), 54, 38, 9130 to 9133 and 9135 are ignored, 9134 and the window apply. An unreadable value: 297=5 CloseContractId(9155) must be a number. |
TransactTime(60) | optional | UTCTimestamp | Accepted, ignored. |
The named-on-absence tags are checked in the order 131, then, on an opening request, 55, 54, 38, 9130, after the client resolver of section 2. Everything else of POST .../rfq applies unchanged and answers 297=5 with its own sentence and code (9110): the offered-market gate (This market is not offered., 4004), the tick and lot grids, the market's minimum quantity and its minimum request value at the current mark (revision 8, docs/11 revision 12, the venue's ADR-25: The request value is below the minimum for this market (100.00 USD). with 9110=4000, refused before a row like the grids, so no 9140 and no rfq.rejected webhook; a request the venue still refused for its minimum after the row keeps 297=5 with the same sentence, 9140 and 9160=REJECTED, and a close request the venue refused for it reads This contract is worth less than the market minimum (100.00 USD) at the current mark; it ends at its barrier, its take profit or its expiry.), the barrier floor, the verification gate (4030 with 9118), the TERMS re-consent gate (4035 with the sentence, no 9118; revision 6, an opening request only, never a close request), the partner close-only state of the settlement tier (4041 with the sentence, no 9118; revision 6, docs/11 15.7, an opening request only, never a close request), the key information document gate and the strict consent gate (revision 6 grown in place, docs/11 section 4 and 12.2: both 4035 with their sentence in Text(58) and no 9118; the KID gate on a RETAIL client's opening request without the acknowledged current document of the market and variant, refused while none is published, never on a close request; the strict consent gate on a strict take profit request while the current Risk Disclosure version is not accepted; the acknowledgement is recorded over REST, POST .../consents, and the next request passes), the account state, the restrictions and halts, the disabled market, the free-margin check. The request row carries channel FIX, the key of your session and your 131; the rate limit of section 1 (the shared trade_per_minute bucket) answers 297=5 Too many requests. Please slow down and try again. 9110=4290.
7.1.1 QuoteReqID(131) reuse, the Idempotency-Key semantics of docs/11 section 5 on the same store: the reference is CLAIMED before the request is built and the outcome kept for 24 hours under the client it named. Reused with an IDENTICAL payload (the same 55, 54, 38, 9130 to 9136, 9155 and the same client): nothing is created; the request's CURRENT state is restated as 35=AI with 9117 Duplicate=Y, 9140 RfqId and 9110=4092 (the original refusal again when the first use was refused before a row). Reused with a DIFFERENT payload: 35=AI 297=5 QuoteReqID reused with a different payload 9110=4092, nothing created. Reused while the first use is still in flight (a second session of yours): the second waits up to 5 seconds for the stored outcome and answers as above; past that it answers 297=5 A request with this QuoteReqID is still in progress. 9110=4092. Read the state back before retrying. Once the request reached the venue the reference is kept whatever fails on our side afterwards (revision 11, docs/11 revision 19): the request is answered with its creation report from the row (297=10 with 9110=5020 while the row carries no venue id yet, else 297=16) and a reuse restates it; it never opens a second request at the venue.
| Tag | Presence | Content |
|---|---|---|
QuoteReqID(131) | when the request was made over FIX (a gateway-built report always) | Your reference. Absent on a drop-copy delivery of a request made over REST: 9140 identifies it. |
QuoteStatus(297) | always | 16 Active (the request is OPEN at the venue: at creation and on a restatement), 0 Accepted (the request traded), 7 Expired, 17 Canceled, 5 Rejected (a refusal before a row, or the venue's refusal after it: 58 the sentence), 10 Pending (the venue could not be reached at creation or, since revision 13, answered that it could not confirm the request: the row is OPEN without a venue id and our poll resolves it; the REST 502 code 5020). 7.9. |
9140 RfqId | when a row exists | Our request id (rfqId). |
9161 VenueRfqId | when known | The venue's request id. |
9160 RfqState | when a row exists | The record's state verbatim: OPEN, ACCEPTED, CANCELLED, EXPIRED, REJECTED. |
Symbol(55) | when a row exists, or echoed | The market; on a refusal before a row, what you sent. |
Side(54) | when a row exists, or echoed | 1 BUY / 2 SELL; on a close request the CLOSING side; on a refusal before a row, what you sent (only inside the FIX enumeration). |
OrderQty(38) | when a row exists, or echoed | The quantity; on a refusal before a row, what you sent. |
9130 BarrierLevel | on an opening request | The knock-out price as stored. |
9131 TakeProfit | on an opening request with one | The take profit as stored. |
9132 StrictTakeProfit | on an opening request | Y / N. |
9144 ContractExpiryTs | on an opening request | The effective contract expiry, Unix ms. |
9149 ExpiryDefaulted | on an opening request | Y when the 12-month default was applied, else N. |
9155 CloseContractId | on a close request | The contract (echoed on a refusal before a row). |
9134 AutoAccept | when a row exists | Y / N. |
ExpireTime(62) | when a row exists | The quoting window's end as the venue confirmed it (expiresAt), UTC. |
9152 Premium | on an opening request | The record's premium (the maximum loss). |
9143 PremiumBps | on an opening request | The record's premiumBps (the share of the entry-to-barrier distance the request trades at). |
9146 ImpliedLeverage | on an opening request when computable | The record's impliedLeverage (at the traded entry once accepted, else at the mark the request was valued at). |
9147 ImpliedPotentialProfit | on an opening request with a take profit | The record's potentialProfit at the same reference. |
9139 MarkAtRequest | on an opening request when the mark was fresh | The record's markAtRequest. |
9150 ContractId | on 297=0 | The contract. |
9151 Entry | on 297=0 | The entry price (on a close request, the settlement price). |
9162 AcceptedQuoteId | on 297=0 | The accepted quote. |
Text(58) | on 297=5, 297=10 and a restatement with a note | The sentence: the refusal, The trading venue cannot be reached right now. Please try again shortly. on 297=10, the request's autoAcceptNote on a restatement when one is set. |
9110 Code | on 297=5, 297=10 and a replay | 4000, 4004, 4030, 4035, 4041, 4290, 5020; 4092 on a replay. |
9114 VenueRejectCode | on the venue's refusal | The venue's numeric code (the webhook's rejectCode). |
9118 MissingBlocks | on a 4030 refusal | The verification gate's missingBlocks, comma joined. |
9115 SourceRef | on every hook-built report | rfq:311:accepted, rfq:311:expired, rfq:311:canceled, rfq:311:rejected: the webhook's source_ref, the dedupe key. Absent on the creation report and on a replay. |
9117 Duplicate | on a replay | Y (7.1.1). |
9121 Origin | always | FIX (the request was made on a FIX session of yours), REST (over the Partner API). |
NoPartyIDs(453) | always | 1. |
PartyID(448) | always | The client, the rule of 4.1. |
PartyIDSource(447) | always | D. |
PartyRole(452) | always | 3. |
TransactTime(60) | always | The time the report was built. |
The tags the router commits to on EVERY 35=AI: 297, 60, 453, 448, 447, 452, 9121. A 35=AI carries no ExecID(17). The creation-time report is gateway-built after the request was created and carries everything the record of docs/11 12.2 carries that has a tag above; the state-change reports (297=0, 7, 17, 5 after a row) come from the hook sites with 9115. The creation report is 297=16 (or 297=10) by construction and is sent only while the request is still OPEN when the gateway reads it back: a request that the automatic accept (or the venue) moved before that read gets NO creation report, the hook-built 297=0 (7, 17, 5) alone. In the rare race where the read-back precedes the move and the send follows the hook's report, a creation 297=16 can reach you AFTER a 297=0 of the same request: a 35=AI WITHOUT 9115 is the creation acknowledgement, never a state change, and never supersedes one WITH 9115; key your request state on the reports that carry 9115.
8=FIX.4.4|9=180|35=R|49=SWISSBORG|56=BARRIERS|34=7|52=20260908-09:00:00.700|131=rfq-2001|453=1|448=cust-001|447=D|452=3|146=1|55=BTC-USD-KO|54=1|38=1.0|9130=63000.0|9131=66500.0|9134=N|9136=60000|10=144|
8=FIX.4.4|9=360|35=AI|49=BARRIERS|56=SWISSBORG|34=8|52=20260908-09:00:00.800|131=rfq-2001|297=16|9140=311|9161=5207|9160=OPEN|55=BTC-USD-KO|54=1|38=1.0|9130=63000.0|9131=66500.0|9132=N|9144=1819843200000|9149=Y|9134=N|62=20260908-09:01:00.000|9152=2222.130000|9143=9000|9146=26.51|9147=1031.000000|9139=65469.0|9121=FIX|60=20260908-09:00:00.800|453=1|448=cust-001|447=D|452=3|10=107|
From the rfq.quote hook site, once per quote id: a quote the writer refreshes under the same id fires no second 35=S (the webhook's own rule); the accept is by quote id at the venue's current price for that id, bounded by our acceptability rules at accept.
| Tag | Presence | Content |
|---|---|---|
QuoteID(117) | always | Our quote id (quoteId, the venue's). |
QuoteReqID(131) | when the request was made over FIX | Your reference. |
9140 RfqId | always | Our request id. |
Symbol(55) | always | The market. |
Side(54) | always | The REQUEST's side. |
OrderQty(38) | always | The quoted quantity. |
OfferPx(133) | on a BUY request | The quoted entry (the maker offers to the buyer). |
BidPx(132) | on a SELL request | The quoted entry (the maker bids). On a close request the row's side is the CLOSING side: a LONG contract closes by selling, so its close quotes carry 132; a SHORT's carry 133. Exactly one of the two is present. |
ValidUntilTime(62) | always | The quote's validity end (validUntil), UTC. |
9141 Premium | on an opening request | The premium this quote would trade at (the quote's premium). |
9143 PremiumBps | always | The premium share this quote trades at (premiumBps). |
9142 Acceptable | always | Y / N by the wire's own rule (acceptable): the barrier side, the mark against the barrier, the leverage cap at the market's barrier floor, the take-profit side, the market's take-profit floor where it publishes one, the off-market guard at the market's published band (0.50 percent unless the venue sets a tighter one), the premium budget; on a close request the price strictly inside the contract's terms. |
9145 NotAcceptableReason | on 9142=N | The sentence (notAcceptableReason), the same one an accept would answer (The current price has already reached the barrier; this quote cannot be accepted., This quote is at or beyond the barrier: the contract cannot close below its guaranteed maximum loss., the deviation, floor and budget sentences; the deviation sentence names the market's band). |
9146 ImpliedLeverage | on an opening request when computable | The leverage at this entry. |
9147 ImpliedPotentialProfit | on an opening request with a take profit | The profit at the take profit from this entry. |
9148 ImpliedPnl | on a close request | The realised P&L in USDC, signed, a close at this price would pay. |
9134 AutoAccept | always | Y / N. On a Y request the router accepts an acceptable quote itself: the 35=S is informational and the 297=0 and the 35=8 of 7.6 follow without a 35=AJ. |
9155 CloseContractId | on a close request | The contract. |
9115 SourceRef | always | rfq:311:quote:902. |
9121 Origin | always | FIX / REST. |
NoPartyIDs(453) | always | 1. |
PartyID(448) | always | The client. |
PartyIDSource(447) | always | D. |
PartyRole(452) | always | 3. |
TransactTime(60) | always | When the quote was mirrored. |
The tags on every 35=S: 117, 55, 54, 38, 62, 9140, 9142, 9134, 9115, 9121, 60, the Parties component. A 35=S carries no ExecID(17). The webhook's pending cap does not apply to FIX; a session that cannot keep up with quotes is a slow consumer (section 6). On the development environment a market maker connected to the venue quotes every request within seconds: a session with several open requests receives a burst of 35=S.
8=FIX.4.4|9=310|35=S|49=BARRIERS|56=SWISSBORG|34=9|52=20260908-09:00:00.900|117=902|131=rfq-2001|9140=311|55=BTC-USD-KO|54=1|38=1.0|133=65471.5|62=20260908-09:00:31.000|9141=2224.350000|9143=9000|9142=Y|9146=26.49|9147=1028.500000|9134=N|9115=rfq:311:quote:902|9121=FIX|60=20260908-09:00:00.900|453=1|448=cust-001|447=D|452=3|10=168|
| Tag | Required | Meaning |
|---|---|---|
QuoteRespID(693) | named on absence | Your reference for the accept, unique per client for 24 hours (the store key of the accept; the shape rule of 131: QuoteRespID(693) must be 1 to 64 characters and must not start with BARR-). A value that begins or ends with whitespace, or is whitespace only, is refused at the door before the request is resolved and before the store is read or written: 35=j 380=0 379 = the value as sent 9110=4000 Text(58) QuoteRespID(693) begins or ends with whitespace and is refused (revision 9). |
QuoteRespType(694) | named on absence | 1 (hit / lift). Any other value: 35=j 380=0 QuoteRespType(694) must be 1. |
QuoteID(117) | named on absence | The quote to accept. An unreadable value: 35=j QuoteID(117) must be a number. |
QuoteReqID(131) or 9140 RfqId | one of them (Reject(35=3) 371=131 when neither: QuoteResponse requires QuoteReqID(131) or RfqId(9140)) | The request the quote belongs to: your own 131 (resolved through the store of 7.1.1 and, whenever the store has no completed outcome for it, past its 24 hours or while the request's first use is still in flight, through the request record from the moment it exists, the rule of 3.3) or our id. A 131 whose first use has not produced a request yet answers 35=j 9110=4004. An unreadable 9140: 35=j RfqId(9140) must be a number. A 131 that begins or ends with whitespace, or is whitespace only, is refused at the door before it is resolved, whether or not 9140 is present: 35=j 380=0 379 = your 693 9110=4000 Text(58) QuoteReqID(131) begins or ends with whitespace and is refused (revision 9). |
Parties | required |
The accept runs the SAME function as POST .../rfq/{rfqId}/accept, with every check: the request open, the quote live and acceptable at a mark read now, the margin admission. On success the gateway sends NOTHING itself: the rfq.accepted hook site delivers the 35=AI 297=0 and the contract-open 35=8 of 7.6 (on a close request: the settlement 35=8 of 7.7, then the 297=0 and the early-close trade). A refusal answers BusinessMessageReject(35=j) 380=0 379=693 Text(58) = the sentence: This request was not found. (9110=4004, also for a 131 the store does not know and for another client's request), This request is no longer open., An acceptance is already in progress for this request., This quote is not available any more. (4004), the TERMS re-consent gate's sentence on the accept of an OPENING request (9110=4035, revision 6; the accept of a close request is never gated), the partner close-only sentence on the accept of an OPENING request (9110=4041, revision 6, docs/11 15.7; a close accept never), the acceptability sentences of 7.3, the margin sentence, the venue's refusal with 9114, The trading venue cannot be reached right now. Please try again shortly. with 9110=5020 and 9163 Accepting=Y (the accept is in flight at the venue: our poll resolves it, read the state back; since revision 13 also when the venue answered that it could not confirm the accept), 9110=4000 otherwise; the request is unchanged. A refused accept is a 35=j and never a 35=AI: the request did not change state.
QuoteRespID(693) reuse: identical (the same 117 and request) after a success replays the contract-open 35=8 with 9117=Y and a dup- ExecID; identical after a refusal replays the same 35=j with 9117=Y; a different payload answers 35=j 380=0 QuoteRespID reused with a different payload 9110=4092; in flight, A request with this QuoteRespID is still in progress. 9110=4092 after the wait.
8=FIX.4.4|9=133|35=AJ|49=SWISSBORG|56=BARRIERS|34=10|52=20260908-09:00:01.000|693=acc-2001|694=1|117=902|131=rfq-2001|453=1|448=cust-001|447=D|452=3|10=186|
8=FIX.4.4|9=418|35=AI|49=BARRIERS|56=SWISSBORG|34=10|52=20260908-09:00:28.000|131=rfq-2001|297=0|9140=311|9161=5207|9160=ACCEPTED|55=BTC-USD-KO|54=1|38=1.0|9130=63000.0|9131=66500.0|9132=N|9144=1819843200000|9149=Y|9134=N|62=20260908-09:01:00.000|9152=2224.350000|9143=9000|9146=26.49|9147=1028.500000|9139=65469.0|9150=4711|9151=65471.5|9162=902|9115=rfq:311:accepted|9121=FIX|60=20260908-09:00:28.000|453=1|448=cust-001|447=D|452=3|10=026|
| Tag | Required | Meaning |
|---|---|---|
QuoteReqID(131) or 9140 RfqId | one of them (Reject(35=3) 371=131 when neither: QuoteCancel requires QuoteReqID(131) or RfqId(9140)) | The request (your 131 resolved as in 7.4: through the store, else through the request record from the moment it exists, so a 35=Z from another session of yours on the first 35=AI of its drop copy is honoured). A 131 that begins or ends with whitespace, or is whitespace only, is refused at the door before it is resolved, whether or not 9140 is present: 35=j 380=0 379 = the value as sent 9110=4000 Text(58) QuoteReqID(131) begins or ends with whitespace and is refused (revision 9). |
QuoteCancelType(298) | named on absence | 5. FIX 4.4 reads 5 as "cancel the quote specified in QuoteID"; on this gateway 298=5 cancels the WHOLE request (every quote of it lapses with the request), the only cancel the router offers. Any other value: 35=j 380=0 QuoteCancelType(298) must be 5. |
QuoteID(117) | optional | Accepted and ignored (the request is canceled, not one quote). |
Parties | required |
The cancel runs POST .../rfq/{rfqId}/cancel. On success the gateway sends nothing itself: the rfq.canceled hook site delivers 35=AI 297=17; a request the venue had already expired reads 297=7 from the same site. A refusal answers 35=j 380=0 with the sentence (This request was not found. 4004, This request is no longer open., An acceptance is already in progress for this request., the venue's refusal with 9114, the venue unreachable with 5020). Since revision 13 a cancel the venue answered that it could not confirm (its code 5000, or a 5xx without a venue code) is never refused: it answers the transport answer, 35=j 380=0 9110=5020 without 9114, with The trading venue could not confirm whether the cancel took effect. Please check whether it is still open and cancel it again if it is.; the request is unchanged until our poll sees the venue's state, which the hook site then reports (297=17 when the cancel took effect).
From the rfq.accepted hook site, one report per accepted request: the contract open on an opening request, the closing TRADE on a close request (the settlement of the contract is 7.7's report). The table below is the one registry of the three contract reports (the open, the early-close trade and the end of 7.7), one row per tag in the code's order; the Presence column says on which of them the tag rides.
| Tag | Presence | Content |
|---|---|---|
OrderID(37) | always, 0 | No order exists; the venue prints RFQ executions the same way. |
ClOrdID(11) | when the opening request was made over FIX | The opening request's QuoteReqID(131); absent on a request made over REST. |
ExecID(17) | always | Gateway-built (rfq- on the open and the early-close trade, bar- on the end, dup- on a replay). |
ExecType(150) | always, F | A trade. |
OrdStatus(39) | always, 2 | Filled. |
Symbol(55) | always | The KO market. |
Side(54) | always | On the open the request's side; on the early-close trade and on the end the CLOSING side: a LONG closes with 2, a SHORT with 1. |
OrderQty(38) | always | The quantity. |
LastQty(32) | always | The quantity. |
LastPx(31) | when the close has a price; always on the open | The entry price on the open, the close price on the early-close trade, the settlement price on the end (absent, never 0, on a tear-up reported without a price). |
CumQty(14) | always | The quantity. |
LeavesQty(151) | always, 0 | Nothing is left. |
AvgPx(6) | when the close has a price; always on the open | The same price as 31. |
Commission(12) | on the open and the early-close trade | The opening commission in USDC on the open (basis points of the premium at the client's BARRIER scheme, OQ-36 D16); on the early-close trade our early-close commission (BROKER-14, revision 6 grown in place: the same scheme on the premium at the accepted close price; 0 before that revision). |
CommType(13) | with 12 | 3 (absolute). |
Currency(15) | with 12 | USDC. |
9111 TradeId | when known | The venue trade id of the opening trade, or of the close trade (absent on the end when the venue's close frame settled the contract first, docs/11 16.4). |
9140 RfqId | when known | The request (on the end: the opening request, when one exists). |
9161 VenueRfqId | on the open and the early-close trade, when known | The venue's request id (on the early-close trade: the close request's). |
9162 AcceptedQuoteId | on the open and the early-close trade | The accepted quote (on the early-close trade: the accepted close quote). |
9150 ContractId | always | The contract. |
9151 Entry | always | The entry price (on the early-close trade the close price, the same as 31; on the end the contract's entry). |
9155 CloseContractId | on the early-close trade and the early-close end | The contract, so the two 35=8 of an early close correlate. |
9152 Premium | on the open and the end | The contract's premium (the maximum loss). |
9153 Commission | on the open and the end | The opening commission (basis points of the premium at the client's BARRIER scheme); the early-close commission travels in Commission(12) of the early-close trade. |
9154 Direction | always | LONG / SHORT, the contract's holder direction. |
9130 BarrierLevel | on the open and the end | The contract's terms as frozen at the open, read from the contract record. |
9131 TakeProfit | on the open and the end, with one | The take profit. |
9132 StrictTakeProfit | on the open | Y / N. |
9144 ContractExpiryTs | on the open | The effective contract expiry, Unix ms. |
9143 PremiumBps | on the open | The premium share the contract trades at. |
9156 CloseReason | on the end | knockOut, takeProfit, expiry, earlyClose, tearUp, other (the webhook's reason). |
9164 VenueCloseReason | on the end when it differs | The record's closeReason verbatim when it differs from 9156 (liquidation, finalSettlement, reconciler:ledger). |
9157 SettlePrice | on the end when the close has a price | The settlement price (the same as 31). |
9158 Pnl | on the end | The realised P&L in USDC, signed. |
9159 ClosedAt | on the end | Unix ms. |
9115 SourceRef | always | rfq:311:accepted on the open and the early-close trade, barrier:4711:closed on the end. |
9117 Duplicate | on a replay | Y (7.4). |
9121 Origin | always | FIX / REST: the opening request's channel. |
NoPartyIDs(453) | always | 1. |
PartyID(448) | always | The client (the rule of 4.1). |
PartyIDSource(447) | always | D. |
PartyRole(452) | always | 3. |
TransactTime(60) | always | The accept time on the open and the early-close trade, the close time on the end. |
On a CLOSE request's acceptance the same shape reports the closing trade: 54 the closing side, 31 / 6 / 9151 the close price, 9155 CloseContractId = the contract, 9154 the contract's direction, 9111 the close trade id, 9140 the close request, 9161 the close request's venue id and 9162 the accepted close quote, 12 our early-close commission (since revision 6 grown in place, BROKER-14: the client's BARRIER scheme on the premium at the accepted close price; 0 before), no 9152, no 9153, no terms. It is the trade confirmation; the settlement is 7.7.
8=FIX.4.4|9=459|35=8|49=BARRIERS|56=SWISSBORG|34=11|52=20260908-09:00:28.000|37=0|11=rfq-2001|17=rfq-1788483628000-5|150=F|39=2|55=BTC-USD-KO|54=1|38=1.0|32=1.0|31=65471.5|14=1.0|151=0|6=65471.5|12=22.243500|13=3|15=USDC|9111=771310|9140=311|9161=5207|9162=902|9150=4711|9151=65471.5|9152=2224.350000|9153=22.243500|9154=LONG|9130=63000.0|9131=66500.0|9132=N|9144=1819843200000|9143=9000|9115=rfq:311:accepted|9121=FIX|60=20260908-09:00:28.000|453=1|448=cust-001|447=D|452=3|10=134|
From the barrier.closed hook site, one report per contract, whatever ended it: the knock-out, the take profit, the expiry, an early close, a tear-up. Its tags are the rows of the table in 7.6 whose Presence says "on the end" or "always": OrderID(37) 0, ClOrdID(11) = the OPENING request's QuoteReqID(131) when it was made over FIX, a bar- ExecID, 150=F 39=2, the KO symbol, Side(54) = the CLOSING side (a LONG closes with 2, a SHORT with 1), the quantity in 38 / 32 / 14 with 151=0, the settlement price in 31 / 6 / 9157 SettlePrice (all three ABSENT when the close has none, a tear-up reported without a price; 9156 still says why; omitted rather than 0, which would read as a price), 9150 ContractId and 9140 RfqId (the opening request, when one exists), 9154 Direction, 9156 CloseReason (knockOut, takeProfit, expiry, earlyClose, tearUp, other, the webhook's reason), 9164 VenueCloseReason when the record's own word differs (liquidation, finalSettlement, reconciler:ledger), 9158 Pnl (the realised P&L in USDC, signed), 9152 Premium and 9153 Commission (the contract's premium and opening commission), 9159 ClosedAt (Unix ms), the terms 9151 Entry, 9130 BarrierLevel and 9131 TakeProfit (with one), 9111 TradeId = the close trade id when known (absent when the venue's close frame settled the contract first, docs/11 16.4), 9155 CloseContractId on an early close (so the two 35=8 of an early close correlate), 9115 SourceRef = barrier:4711:closed, 9121 Origin = the opening request's channel, the Parties component and TransactTime(60) = the close time.
The tags on every contract 35=8 (7.6 and 7.7): 37, 17, 150, 39, 55, 54, 38, 32, 14, 151, 60, the Parties component, 9150, 9154, 9115, 9121.
Routing (section 5 applied to contracts): the contract-end report reaches the key that OPENED the contract (the opening request's key), the key that requested the early close when that is another key of yours, and every drop-copy session; a contract opened over REST reports to drop-copy sessions only. On an early close accepted over FIX the settlement is booked BEFORE the request is marked accepted, so the 35=8 of 7.7 arrives first, then the 297=0, then the early-close trade of 7.6: correlate the three by 9155 and 9150, never by order.
8=FIX.4.4|9=433|35=8|49=BARRIERS|56=SWISSBORG|34=12|52=20260908-11:00:00.000|37=0|11=rfq-2001|17=bar-1788490800000-6|150=F|39=2|55=BTC-USD-KO|54=2|38=1.0|32=1.0|31=63000.0|14=1.0|151=0|6=63000.0|9140=311|9150=4711|9154=LONG|9156=knockOut|9157=63000.0|9158=-2224.350000|9152=2224.350000|9153=22.243500|9159=1788490800000|9151=65471.5|9130=63000.0|9131=66500.0|9115=barrier:4711:closed|9121=FIX|60=20260908-11:00:00.000|453=1|448=cust-001|447=D|452=3|10=110|
rfq.quote to 35=S; rfq.accepted to 35=AI 297=0 plus the 35=8 of 7.6; rfq.expired, rfq.canceled, rfq.rejected to 35=AI 297 7 / 17 / 5; barrier.closed to the 35=8 of 7.7. Every one of them is built inside the router's transaction and released after the commit (section 6). The gateway itself builds only the creation-time 35=AI, the replays and the refusals of 7.1.1, 7.4 and 7.5. Nothing about the barrier module's own behaviour changes: the automatic accept runs as it does for REST and its accept reaches FIX through the same rfq.accepted site.
QuoteStatus(297) values chosenOf FIX 4.4's enumeration the router uses six: 16 Active, 0 Accepted, 7 Expired, 17 Canceled, 5 Rejected, 10 Pending; every other value never appears. 9160 RfqState carries the router's own state beside it so nothing is lost in the mapping (CANCELLED is spelled with two Ls on the REST wire and rides verbatim).
Three clauses, the venue's, kept word for word:
Reject(35=3) with SessionRejectReason(373)=1, RefTagID(371) = the tag, RefMsgType(372) = the type, RefSeqNum(45) and a Text(58) naming it, decided before anything is built, so nothing reaches the router's functions and no report follows.9110.The one exception: Side(54) outside the FIX 4.4 enumeration (1 to 9, A to G) answers 35=3 373=5, because an echoed 54 would make the router's own ExecutionReport invalid FIX.
| Situation | Answer |
|---|---|
| Anything before a Logon; a Logon that fails a check | Logout(35=5) and the close (section 1) |
| A MsgSeqNum gap; malformed framing | Logout(35=5) and the close (section 1) |
A TestRequest(35=1) without TestReqID(112) | Reject(35=3) 373=1 371=112, no Heartbeat (section 1) |
A second Logon(35=A) on a live session | Reject(35=3) 373=99 372=A, Text(58) logon repeated on a live session: a re-logon with 141=Y needs a new connection; the session stays up |
A message without MsgType(35) | Reject(35=3) 373=1 371=35 (the one case where 372 is omitted rather than sent empty) |
A 35= value that is not a FIX 4.4 MsgType | Reject(35=3) 373=3 372 echoed, Text(58) unsupported MsgType <value> |
A tag value carrying a byte above 0x7F (any message type) | Reject(35=3) 373=6 371 = the tag, 372 echoed, Text(58) non-ASCII value in tag <tag>: FIX tag values are ASCII; the session stays up (section 1). Before the Logon: Logout(35=5) malformed message: ... and the close |
A FIX 4.4 application message the gateway does not implement (35=V, 35=AE, 35=b, ...) | BusinessMessageReject(35=j) 380=3 (unsupported message type) 372 echoed, Text(58) naming where the facts live (Market data is not carried over FIX: read GET /partner/v1/ticker, /orderbook, /market-trades and /klines for the market-data family) |
35=G | OrderCancelReject(35=9) 434=2 102=99 (section 3) |
A missing or malformed Parties; an unknown or foreign client | 35=j 380=5 / 380=1 (section 2) |
| A message that enters an order or a request on a drop-copy session | Reject(35=3) 373=99 (section 5) |
A required tag absent or empty on a 35=D (11, 55, 54, 38, 40), a 35=H with neither 41 nor 37, a 35=R (131, and on an opening request 55, 54, 38, 9130), a 35=AJ (693, 694, 117), a 35=Z (298), a 35=AJ or 35=Z with neither 131 nor 9140 | Reject(35=3) 373=1 naming it (sections 3 and 7) |
A Side(54) outside the FIX 4.4 enumeration on a 35=D or an opening 35=R | Reject(35=3) 373=5 371=54 (3.1, 7.1) |
A reference that begins or ends with whitespace, or is whitespace only: 11 on a 35=D, 41 on a 35=F or 35=H, 131 on a 35=R, 35=AJ or 35=Z, 693 on a 35=AJ (revision 9) | Refused at the door on the message's own reject shape, before the store, the record or the venue is reached, Text(58) naming the tag (ClOrdID(11) begins or ends with whitespace and is refused and its three siblings for 41, 131 and 693), 9110=4000, the value echoed as sent: 35=8 150=8 103=99 for the 35=D, 35=9 102=99 for the 35=F, 35=AI 297=5 for the 35=R, 35=j 380=0 for the 35=H, 35=AJ and 35=Z (3.1, 3.3, 3.5, 3.7, 7.1, 7.4, 7.5); nothing is trimmed |
A 35=F with neither 41 nor 37 | 35=9 102=1 naming the two tags (a valid 35=9 can be built, so no 35=3) |
| A 35=D refused by the router before the venue | 35=8 150=8 gateway-built (3.4) |
| A 35=D the venue refused | 35=8 150=8 from the hook site with the order's id (3.2) |
| A 35=F refused | 35=9 (3.5) |
| A 35=F the venue answered that it could not confirm (its code 5000 or a 5xx without a venue code, revision 13) | 35=j 380=4 9110=5030 (3.5, 8.3), never a 35=9 |
| A 35=H on an order the client does not hold | 35=8 150=I 39=8 103=5 (3.7) |
| A 35=R refused before a row (a shape rule, a gate, the module's sentence) | 35=AI 297=5 gateway-built, no 9140 (7.2) |
| A 35=R the venue refused after the row | 35=AI 297=5 from the hook site with 9140, 9114 and 9115 (7.2) |
| A 35=R the venue could not be reached for | 35=AI 297=10 with 9140 and 9110=5020 (7.2) |
| A 35=D or a 35=R the venue answered that it could not confirm (its code 5000 or a 5xx without a venue code, revision 13) | the pending outcome: nothing for an order (3.2), the creation report 297=10 for a request (7.2) |
| A 35=Z the venue answered that it could not confirm (revision 13) | 35=j 380=0 9110=5020 with the cancel sentence, no 9114 (7.5) |
| A 35=AJ or a 35=Z refused | 35=j 380=0 with the sentence, 379 = your reference, 9110 (7.4, 7.5) |
| The rate limit | The message's business reject with Too many requests. Please slow down and try again. and 9110=4290 (a 35=8 150=8 103=99 for a 35=D, a 35=9 102=99 for a 35=F, a 35=AI 297=5 for a 35=R, a 35=j 380=0 for a 35=H, 35=AJ or 35=Z) |
| An exception after the router's function may have written, before any venue call | 35=j 380=4 (8.3), never a fabricated 35=8, 35=9 or 35=AI |
| An exception of ours after a 35=D or a 35=R reached the venue (revision 11) | the pending outcome: nothing for an order (3.2), the creation report for a request (7.1.1); the reference is kept (3.3) |
Every reject carries RefSeqNum(45) (35=3, 35=j) or the message's own references (35=8, 35=9, 35=AI) so you correlate it; a 35=3 carries no 11 and no 17: correlate it by 45.
35=j 380=4 (application not available) with 379 = the message's reference, Text(58) The request could not be completed; read the state back before retrying. and 9110=5030, and NEVER a 35=8 150=8 or a 35=9: the order or the cancel may exist, and its truthful report comes from the hook sites when our reconciliation resolves it. Read GET .../orders/open before retrying after a 380=4, the same advice the REST guide gives after a 502. The session stays up. Since revision 11 this answers a throw BEFORE the venue call alone: once a 35=D or a 35=R reached the venue, a throw of ours is answered as its pending outcome (the next bullet for an order, the creation report for a request, 7.1.1) and its reference is never given back (3.3). Since revision 13 the same 35=j answers a 35=F the venue could not confirm (its code 5000, or a 5xx without a venue code, which our venue client raises as a transport failure): the cancel may exist (3.5).Recon:IntervalSeconds, 300 by default; 3.2 and 4.5).Margin state and the risk-state transitions (poll GET .../margin or take the margin.* webhooks), collateral and settlement (/collateral, /settlement/*, the collateral.* and settlement.* webhooks), onboarding and KYC, MiFID appropriateness (the questionnaire, its announced versions and the served languages of docs/11 revision 17 included: FIX carries neither appropriateness_test.version_changed nor appropriateness_test.text_changed, and no sink for them is offered on this gateway), categorisation, statements, market data (GET /partner/v1/ticker, /orderbook, /market-trades, /klines, /instruments, /market-info; a 35=V answers 35=j 380=3), staking swaps (POST .../staking/{offerId}/accept and its reads), attach and OCO legs and the leverage hint (place brackets over REST), and cancel-on-disconnect (the router cancels nothing on a partner's disconnect; your own risk controls and our close-out engine stand) remain on the Partner API and the webhooks. There is no resend store and no amend. The app order commands of docs/11 section 22 (revision 12) have no FIX twin either: your app sends them over REST with the token you sign, and FIX only reports their orders to your drop-copy sessions (section 5). FIX is order entry, execution reporting and the barrier RFQ vocabulary: run it alongside a REST client and the webhooks for everything else.
141=Y, 553 = your key prefix, 554 = the key, 49 = your partner code, 56=BARRIERS, 108 between 5 and 120; expect the Logon echo as the FIRST frame of the session, 34=1, with 9120=N. Repeat with a wrong 554: expect Logout Sign in again. with 9110=4010 and 56=CLIENT, then a closed socket. Repeat with a READ key: expect the 4034 sentence. Repeat from an address off your allowlist: expect the SAME Sign in again. as a wrong key, and prove your client does not read the two apart.TestRequest answered by Heartbeat with your TestReqID(112). Send one without 112: expect Reject(35=3) 371=112 and no Heartbeat, and a session that stays up.HeartBtInt: expect a TestRequest; answer it. Stay silent for 2x: expect Logout heartbeat timeout.Logout session already active on the second and nothing on the first. Drop the first without a Logout, wait twice its HeartBtInt, log on again with the same key: expect the Logon echo.Logout naming the REST route, then a closed socket. Re-logon with 141=Y and prove your client treats the router's 34 as starting at 1 again.ResendRequest(35=2): expect a SequenceReset(35=4) 123=Y whose 34 is the next number of the router's outbound sequence and whose 36 is the one after it; prove your client does not wait for a resend.Parties component: expect 35=j 380=5 with the sentence and your reference in 379. Send it naming a client you do not hold: expect 35=j 380=1 Unknown client. 9110=4004. Prove your client correlates a 35=j by 45 and 379.35=G: expect 35=9 434=2 102=99 with the sentence. Send it with 37=, 11= and 41= all EMPTY: expect 37=0, 11=BARR-NONE, 41=BARR-NONE, all three PRESENT, and confirm your session layer accepts the answer.35=V: expect 35=j 380=3 naming the REST market-data routes. Send a 35=ZZ: expect Reject(35=3) 373=3.9120=Y on a second key: expect the echo 9120=Y; send a 35=D: expect Reject(35=3) 373=99; send a 35=H: expect an application answer, never a 35=3.GET /partner/v1/me while a session is live: expect fix.enabled true, fix.port the port you connected to, and your session's key prefix in fix.sessions; after your Logout, an empty list (fix.enabled reads false with fix.port null only while our acceptor is not listening on that host).Logout router restart, reconnect with backoff, re-logon with 141=Y.35=D with 40=2, 59=1, your ClOrdID(11), the Parties component): expect 35=8 150=0 39=0 with our 37, your 11, 151 = the quantity, 9113=ACKED, 9115 and 9121=FIX. Read GET .../orders/open: expect channel: "FIX" and channelRef = your reference on the record.OrigClOrdID(41) (35=F): expect NOTHING until the venue confirms, then 35=8 150=4 39=4 58=canceled with 11 and 41 = your reference. Cancel it again by 37: expect 35=9 102=0 This order has already been cancelled..35=8 150=F with 32, 31, 14, 151, 6, 12, 9111 and 9112; on a partial fill 39=1 then 39=2, 6 the volume-weighted average.35=8 150=8 37=0 103=13 9110=4000 with the sentence verbatim, and no order on GET .../orders/open. Enter one for a client below the market's verification level: expect 9110=4030 and 9118.35=D twice: expect 150=0 then 150=I 9117=Y with the same 37. Send it a third time with another price: expect 150=8 103=6 ClOrdID reused with a different payload 9110=4092 and no second order.35=H by 41 for each state you can reach (working, partially filled, being cancelled, filled, cancelled): expect one 150=I each with 39 per 4.2 and 9113 the router's state. Send one for an id you do not hold: expect 150=I 39=8 103=5 37=0.11, 55, 54, 38, 40 in turn: expect Reject(35=3) 373=1 naming the tag, no report, and a session that stays up. Send 54=X: expect 373=5.9121=FIX, the report of an order placed over REST with 9121=REST and no 11, and nothing of another partner's clients.35=H that folds the missed fill into 14 and 6.35=R on a KO market with 9134=N (your QuoteReqID(131), the Parties component, 55, 54=1, 38, 9130, optionally 9131): expect 35=AI 297=16 with 9140, 9161, 9160=OPEN, the terms, 62, 9152, 9143 and 9121=FIX. Read GET .../rfq/{rfqId}: expect channel: "FIX" and channelRef = your reference.35=S per quote id with 117, 133 (a BUY request), 62, 9141, 9142 and 9115; prove your engine dedupes a repeated 9115 and handles a burst of several 35=S.35=AJ (693, 694=1, 117 = a quote with 9142=Y, 131): expect 35=AI 297=0 with 9150, 9151, 9162 and 9115, then 35=8 150=F 39=2 on the KO symbol with 37=0, 11 = your reference, 31 = the entry, 12, 9150 to 9154 and 9130 to 9144. Send the same 35=AJ again: expect the 35=8 again with 9117=Y and a dup- ExecID, and no second contract on GET .../barriers.35=R without 9134: expect 297=16 with 9134=Y, then, when an acceptable quote arrives, the 35=S, the 297=0 and the 35=8 without any 35=AJ from you.35=R with 9136=1000 and let the window pass: expect 35=AI 297=7 9160=EXPIRED 9115=rfq:<id>:expired. Send a 35=Z on it: expect 35=j 380=0 This request is no longer open.. Send a 35=R and a 35=Z (131, 298=5) while it is open: expect 297=17 9160=CANCELLED.35=R off the terms (an expiry more than 8 hours ahead, a quantity of 0, a PERP symbol): expect 35=AI 297=5 with the sentence verbatim, 9110, no 9140, and nothing on GET .../rfq. Send the same 35=R twice: expect 297=16 then 297=16 9117=Y 9110=4092 with the same 9140; a third time with another 9130: expect 297=5 QuoteReqID reused with a different payload.35=R with 9155 = its contractId and 9134=N: expect 297=16 with 9155, 54 the closing side and no 9130; expect the close quotes with 132 (a LONG) and 9148; accept one inside the terms: expect the settlement 35=8 (9156=earlyClose, 9157, 9158, 9159, 9155), the 297=0 and the early-close trade 35=8 (9155, 9161 and 9162 the close request's venue id and the accepted close quote, 12 = our early-close commission with 13=3 15=USDC, the client's BARRIER scheme on the premium at the accepted close price, the same figure as the close's COMMISSION ledger item with reason: "earlyClose" (docs/11), no 9152, no 9153, no terms), in that order. Accept a quote at the barrier first: expect 35=j with the barrier sentence and an unchanged request.35=8 150=F 39=2 on the KO symbol with 54 the closing side, 31 = 9157, 9156=knockOut, 9158, 9159, 9115=barrier:<id>:closed, on the session whose key opened it and on your drop-copy session; on a drop-copy session, expect the reports of a request made over REST with 9121=REST and no 131.| to SOH): every one carries a real BodyLength(9), CheckSum(10) and SendingTime(52) computed over its bytes, so a parser that refuses one of them is wrong about the framing, not the example.35=D whose ClOrdID(11) carries a byte above 0x7F: expect Reject(35=3) 373=6 371=11, no report, no order on GET .../orders/open, and a session that stays up.37: expect nothing until the venue confirms, then the 150=4 with 9121=REST on THAT session and on your drop copy, and nothing on another trading session of yours.150=F first and then a 150=0 whose 39, 14 and 151 match the fill, never a 39=0 after a 39=2.OrigClOrdID(41) on the first 150=0 of your drop copy: expect silence on A, then the 150=4 on A, on B and on the drop copy, once each, and never 35=9 102=1 9110=4004 for an order whose acknowledgement you hold.35=D whose ClOrdID(11) ends in a blank (a space as the last character of the value): expect the gateway-built 150=8 103=99 9110=4000 Text(58) ClOrdID(11) begins or ends with whitespace and is refused with 11 echoed as sent, no order on GET .../orders/open, and the same message without the blank accepted. Then cancel that order by OrigClOrdID(41) with a trailing blank: expect 35=9 102=99 9110=4000 OrigClOrdID(41) begins or ends with whitespace and is refused and the order still open; the same 35=F without the blank cancels it. The same door on 131 (a 35=R answers 297=5, a 35=AJ or 35=Z a 35=j) and on 693 (a 35=j).priceBandBps of a perpetual (GET /market-info), then send a 35=D 40=4 54=2 with a StopPx(99) under the mark and a Price(44) far below the band around it (for example 44=1): expect the gateway-built 150=8 103=99 9110=4000 with the conditional limit band sentence of 3.4 naming the bounds, nothing placed; the same message with Price(44) inside the bounds is accepted (150=0, 9113=PENDING_TRIGGER) (revision 15).The router's custom tags live in 9101 to 9199 and none of them is in the venue's own list (9001 to 9028), so a partner that also connects to the Kronos X venue can load both dictionaries side by side. The registry in code is PartnerFixTags.Registry; this table is generated from it row for row (the tag, the name, the direction, the message types it rides on, the meaning). The same build generates the Tag and Presence columns of the four outbound tag tables (4.1 for the 35=8 of an order, 7.2 for the 35=AI, 7.3 for the 35=S, 7.6 for the three contract 35=8) from the router's presence registries, in the code's order, and computes BodyLength(9), CheckSum(10) and the SendingTime(52) of every example frame of this document over the frame's own bytes; our test suite refuses a build in which any of them drifted from the code, and it checks that every custom tag on an example rides on a message type the registry lists for it. 9165 is reserved and never sent in this revision.
| Tag | Name | Direction | On | Meaning |
|---|---|---|---|---|
| 9101 | TriggerKind | in, out | D, 8 | STOP (default) or TAKE_PROFIT with 40=3 or 4. |
| 9102 | ReduceOnly | in, out | D, 8 | Y / N. |
| 9103 | TriggerSource | in, out | D, 8 | LAST, MARK, INDEX. |
| 9104 | TriggerDirection | in, out | D, 8 | AT_OR_ABOVE, AT_OR_BELOW (derived when absent). |
| 9110 | Code | out | 8, 9, 5, j, AI | Our numeric code (4000, 4004, 4010, 4030, 4034, 4035, 4037, 4041, 4043, 4092, 4290, 5020, 5030). |
| 9111 | TradeId | out | 8 | The venue trade id. |
| 9112 | RealizedPnl | out | 8 | The fill's realised P&L, USDC, signed. |
| 9113 | RouterState | out | 8 | The order row's state verbatim. |
| 9114 | VenueRejectCode | out | 8, 9, AI, j | The venue's numeric code. |
| 9115 | SourceRef | out | 8, S, AI | The event's source_ref (the webhook dedupe key). |
| 9116 | FillRole | out | 8 | MAKER / TAKER. |
| 9117 | Duplicate | out | 8, AI, j | Y on a replay of a reused reference. |
| 9118 | MissingBlocks | out | 8, AI | The verification gate's missingBlocks, comma joined. |
| 9120 | DropCopy | in, out | A | Y = drop-copy session. |
| 9121 | Origin | out | 8, S, AI | FIX, REST, APP, CLOSE_OUT. |
| 9130 | BarrierLevel | in, out | R, AI, 8 | The knock-out price. |
| 9131 | TakeProfit | in, out | R, AI, 8 | The take profit. |
| 9132 | StrictTakeProfit | in, out | R, AI, 8 | Y / N. |
| 9133 | ExpiryTs | in | R | The requested contract expiry, Unix ms. |
| 9134 | AutoAccept | in, out | R, AI, S | Y (default) / N. |
| 9135 | PremiumBudget | in | R | The premium budget. |
| 9136 | ExpiresInMs | in | R | The quoting window. |
| 9139 | MarkAtRequest | out | AI | The mark at the request. |
| 9140 | RfqId | in, out | AJ, Z, AI, S, 8 | Our request id. |
| 9141 | Premium | out | S | The quote's premium. |
| 9142 | Acceptable | out | S | Y / N. |
| 9143 | PremiumBps | out | AI, S, 8 | The premium share. |
| 9144 | ContractExpiryTs | out | AI, 8 | The effective contract expiry, Unix ms. |
| 9145 | NotAcceptableReason | out | S | The sentence. |
| 9146 | ImpliedLeverage | out | AI, S | The implied leverage. |
| 9147 | ImpliedPotentialProfit | out | AI, S | The implied potential profit. |
| 9148 | ImpliedPnl | out | S | On a close request's quote. |
| 9149 | ExpiryDefaulted | out | AI | Y / N. |
| 9150 | ContractId | out | AI, 8 | The contract. |
| 9151 | Entry | out | AI, 8 | The entry price. |
| 9152 | Premium | out | AI, 8 | The contract's premium (maximum loss). |
| 9153 | Commission | out | 8 | The opening commission. |
| 9154 | Direction | out | 8 | LONG / SHORT. |
| 9155 | CloseContractId | in, out | R, AI, S, 8 | An early close and its reports. |
| 9156 | CloseReason | out | 8 | knockOut, takeProfit, expiry, earlyClose, tearUp, other. |
| 9157 | SettlePrice | out | 8 | The settlement price. |
| 9158 | Pnl | out | 8 | The contract's realised P&L, USDC, signed. |
| 9159 | ClosedAt | out | 8 | Unix ms. |
| 9160 | RfqState | out | AI | OPEN, ACCEPTED, CANCELLED, EXPIRED, REJECTED. |
| 9161 | VenueRfqId | out | AI, 8 | The venue's request id. |
| 9162 | AcceptedQuoteId | out | AI, 8 | The accepted quote. |
| 9163 | Accepting | out | j | Y when an accept is in flight at the venue (the REST accepting: true). |
| 9164 | VenueCloseReason | out | 8 | The contract wire's closeReason when it is not the brief's vocabulary. |
| 9165 | Refresh | reserved | S | Reserved for a refreshed quote; never sent in revision 9. |
Standard tags read inbound in this revision: 8, 9, 10, 34, 35, 43, 49, 52, 56, 98, 108, 112, 141, 553, 554 (session); 1, 11, 18, 37, 38, 40, 41, 44, 54, 55, 59, 60, 99 (orders); 62, 117, 131, 146, 298, 693, 694 (RFQ); 447, 448, 452, 453 (Parties); 36, 123 (SequenceReset); 7 and 16 (ResendRequest, ignored beyond parsing). Standard tags written outbound: the header, 58, 45, 371, 372, 373, 98, 108, 141 (the Logon reply), 112 (the Heartbeat answering a TestRequest), 36, 123 (the GapFill SequenceReset answering a ResendRequest) (session), 6, 11, 12, 13, 14, 15, 17, 18, 31, 32, 37, 38, 39, 40, 41, 44, 54, 55, 59, 60, 99, 102, 103, 150, 151, 434 (orders), 62, 117, 131, 132, 133, 297 (RFQ), 379, 380 (business rejects), 447, 448, 452, 453.
| Revision | Date | Change |
|---|---|---|
| 15 | 2026-10-20 | For docs/11 revision 26 (OpenAPI 2.12.6, the conditional limit band, issue #94; the correction of a defect that undermines a regulatory control, shipped at once under docs/11 section 21; no tag or code moved, one refusal added): the venue checks a plain limit price against the market's price band but neither the trigger nor the limit of a conditional order, so a triggered stop limit could trade far from the market or sweep the book past the venue's market-order protection. A NewOrderSingle(35=D) 40=4 whose Price(44) lies outside the market's band (priceBandBps on GET /market-info) around its StopPx(99), or around the mark once the mark has reached the StopPx(99), is now refused before the venue with the gateway-built 150=8 103=99 9110=4000 and the sentence in Text(58) naming the bounds, nothing placed (3.1 Price(44), the 3.4 row; conformance step 37); reduce-only orders too; on a market that publishes a band a 40=4 without a fresh mark answers the existing There is no fresh mark price for ... sentence (103=99 9110=4000), a reduce-only one included where it passed before; 40=3 is unchanged. StopPx(99) itself is not bounded. Derive Price(44) from StopPx(99) and priceBandBps at run time. |
| 14 | 2026-10-22 | For docs/11 revision 25 (the client uid published as an opaque identifier, issue #104; OpenAPI 2.12.5, description strings only; documentation only: no tag, code, value or sentence of this gateway moved and no behaviour changed). Section 2: our client uid in PartyID(448) is OPAQUE (docs/11 section 7): send it exactly as we issued it, letter case and digits as served; the attribution table no longer writes it as BARR- and a fixed run of eight digit placeholders, because no digit count or other format is promised and the format may change for clients created later. Stated as built: a 448 that is one of our uids exactly as issued resolves by uid and any other value is read as your partnerClientRef, so another spelling of a uid is refused with 380=1 9110=4004, and a bare number is read as your own reference, refused only when no client of yours carries it (before this revision the refusal table listed every bare numeric id as refused). The prefix BARR- also begins our order references (BARR-{orderId}, the order id unpadded), our barrier request references (BARR-RFQ-{rfqId}) and our swap references (BARR-SWP-{swapId}), which are not client identifiers and are never sent in 448. |
| 13 | 2026-10-20 | For docs/11 revision 23 (the venue's outcome-unknown answer, issue #69; OpenAPI unchanged; no tag or code moved, one sentence added): the venue answers a write it could not confirm with its code 5000, and a proxy between us and the venue can answer a 5xx without a venue code; either way the write may still take effect. Before, we read such an answer as the venue's refusal: a 35=D answered 150=8 with 9114=5000 and the order REJECTED although it could be live, a 35=R a 297=5, a 35=AJ a 35=j with the request free to be accepted again, a 35=F a 35=9, a 35=Z a 35=j with the venue's refusal. Now each takes the answer of a venue transport failure of the same message: a 35=D answers nothing and its report follows our reconciliation (3.2, 4.5, 8.3), a 35=R the creation report 297=10 (7, 7.2, 8.2), a 35=AJ the in-flight 35=j with 9110=5020 and 9163=Y (7.4), a 35=F the reply policy's 35=j 380=4 9110=5030, never a 35=9, with your session kept as the cancelling key so the 150=4 follows if the venue applied the cancel (3.5, 8.2, 8.3), and a 35=Z a 35=j 380=0 9110=5020 without 9114 and with the new sentence The trading venue could not confirm whether the cancel took effect. Please check whether it is still open and cancel it again if it is. (7.5). The venue's 5001 and every other code answer as before. |
| 12 | 2026-10-13 | For docs/11 revision 21 (OpenAPI 2.12.0, the app order commands of docs/11 section 22; written to follow docs/11 revision 20 of the idempotency body fingerprint, which moved nothing of this gateway): no tag of this gateway moved, one code, one value and one sentence added. Your app may send its users' perpetual orders, cancels, replacements and leverage changes to us directly with a short-lived token you sign; such an order has no session key, so its reports go to your drop-copy sessions only (and to a trading session whose key cancels it), with the new 9121 value APP and without ClOrdID(11) or a derived OrigClOrdID(41), because its channelRef is the token's jti, never a reference of yours (4.1, 4.2, section 5). The new code 4043 SERVER_ORDER_ENTRY_CLOSED: while our operations hold your server-side order entry CLOSED (a switch set only on our compliance function's decision and announced at least 30 days ahead, docs/11 sections 21 and 22.10) a 35=D answers 150=8 103=99 9110=4043 and a 35=F 35=9 102=99 9110=4043, both with the sentence Server-side order entry is closed for this partner: orders, cancels and leverage changes are entered through the app order commands., after the whitespace door and before the rate limit and the store of 3.3, so the reference is not recorded (3.4, 3.5); the 35=H, the barrier messages and the drop copy are untouched. 9110 lists 4043 and 9121 names APP (4.1, Appendix A). Nothing changes while your server-side order entry is ALLOWED, the default. |
| 11 | 2026-09-28 | For docs/11 revision 19 (OpenAPI 2.11.1, the correction of a defect that undermined at-most-once execution; no tag, sentence or code of this gateway moved): before it, a 35=D or a 35=R whose venue call had succeeded and whose next step failed on our side (a database fault) was answered 35=j 380=4 and its ClOrdID(11) or QuoteReqID(131) was given back, so a resend of the same reference reached the venue a second time. Now the reference is kept once the venue was reached (3.3, 7.1.1): the order answers nothing and is restated by a reuse (3.2), the request answers its creation report (7.1.1); 380=4 answers a throw before the venue call alone (8.3). |
| 10 | 2026-09-23 | For docs/11 revision 17 (OpenAPI 2.10.0, the announced questionnaire versions and the language mechanism; no tag, sentence or code of this gateway moved, one sentence added): section 9 states that FIX carries neither appropriateness_test.version_changed nor appropriateness_test.text_changed and offers no sink for them; the questionnaire, its schedule, its languages and the two partner-level events stay on the Partner API and the webhooks. |
| 9 | 2026-09-22 | THE WHITESPACE DOOR, for docs/11 revision 16 (OpenAPI unchanged: no schema, route, field, tag or code moved; four sentences added; the rule the venue takes at Member API 3.72, adopted on this gateway on 2026-09-22): a partner reference that begins or ends with whitespace, or is whitespace only, is refused AT THE DOOR, before our idempotency store is read or written, before the order or request record is read and before the venue is reached, on the message's own reject shape with a sentence naming the tag, the value echoed as sent and never trimmed. ClOrdID(11) on a 35=D: the gateway-built 150=8 103=99 9110=4000 ClOrdID(11) begins or ends with whitespace and is refused (3.1). OrigClOrdID(41) on a 35=F: 35=9 434=1 102=99 9110=4000 with 37 as you sent it or 0 (nothing resolved); on a 35=H: 35=j 380=0 379 = the value 9110=4000; both OrigClOrdID(41) begins or ends with whitespace and is refused (3.3, 3.5, 3.7); a 37 beside the padded reference does not rescue the message. QuoteReqID(131) on a 35=R: 35=AI 297=5 9110=4000, no 9140, an opening and a close request alike; on a 35=AJ or a 35=Z: 35=j 380=0 9110=4000 (379 = your 693 on the 35=AJ, the value on the 35=Z), a 9140 beside it does not rescue the message; all QuoteReqID(131) begins or ends with whitespace and is refused (7.1, 7.4, 7.5). QuoteRespID(693) on a 35=AJ: 35=j 380=0 379 = the value 9110=4000 QuoteRespID(693) begins or ends with whitespace and is refused (7.4). Before this revision such a reference passed the shape rule and was stored as sent, a reference of its own beside its unpadded twin; the door now refuses it, so a 35=D under ABC after ABC places nothing and replays nothing, and a 35=F naming 41=ABC cancels nothing. The length and BARR- rules of 3.1, 7.1 and 7.4 stand unchanged, and whitespace INSIDE a reference stays legal. The 8.2 table carries the row, conformance step 36 the check; pinned red first by PartnerFixOrderTests and PartnerFixRfqTests. |
| 8 | 2026-09-16 | Fractional knock-out contracts and the minimum request value, for docs/11 revision 12 (OpenAPI 2.8.1; the venue's ADR-25 of 2026-09-16, Member API 3.71; docs/08 revision 69; no tag or code moved, one sentence added): a 35=R of an opening request whose OrderQty(38) is worth less than the market's minNotional at the current mark answers 35=AI 297=5 9110=4000 with The request value is below the minimum for this market (100.00 USD). in Text(58), refused before a row like the grids (no 9140, no rfq.rejected webhook, 38 echoed); the market's minQty is checked beside the lot grid (The quantity must be at least 0.0001.); 38 is a decimal on the market's published lot grid, fractional on the knock-out markets once the venue lists them on the perpetual's grid (0.0001 on BTC-USD-KO; 1 stays valid everywhere), echoed unchanged on the reports; a request the venue still refused for its minimum after the row keeps 297=5 with the same sentence, 9140 and 9160=REJECTED; a close request (9155) the venue refused for it reads This contract is worth less than the market minimum (100.00 USD) at the current mark; it ends at its barrier, its take profit or its expiry. (the venue values a close request at the contract's whole quantity; the gateway runs no check of its own on a close). The 7.1 OrderQty(38) row and the refusal list under the table carry the sentences; nothing else moved. |
| 7 | 2026-09-11 | The code audit of 2026-09-11, for docs/11 revision 11 (OpenAPI 2.8.0; no tag, sentence or code moved, no wire change; finding B-458): the presence cells of 4.1, generated from the router's registry, state the one exception the paragraph under the table already named: OrderQty(38), OrdType(40), TimeInForce(59), 9102 ReduceOnly, 9113 RouterState and 9121 Origin read "always, except on the unknown-order status of 3.7" and Symbol(55) and Side(54) "always; on the unknown-order status of 3.7 echoed when the 35=H carried it" (they read "always" before), so a partner validating our reports against the served table no longer rejects a valid unknown-order answer; 3.7 lists the tags that answer omits; our tag-integrity pin holds that contract on a 35=H with and without 55 and 54 instead of skipping the tags there. The served page carries the SHA-256 of the Markdown it was built from (<meta name="source-sha256">, B-455), pinned by our tests, so a stale build fails instead of shipping. Grown in place by the router chunk 6 of the same audit (B-259, documentation only, the documented behaviour of 3.5 and section 5 restored, no tag, sentence or code moved): the key of the cancelling session is recorded on the order BEFORE the venue is asked to cancel, so the 150=4 the venue publishes from the same apply that answers the cancel reaches the cancelling session even when it lands before the answer; a definitive venue refusal (the 35=9 with 9114) leaves the order's routing as it was, and a refused asker receives no later report. The code audit of 2026-09-11, for docs/11 revision 10 (no tag, sentence or code moved, no OpenAPI change): OrigClOrdID(41) on a 35=F or 35=H, and QuoteReqID(131) on a 35=AJ or 35=Z, resolve through the order or request record whenever the store has no completed outcome for the reference, not only past the store's 24 hours but also while the reference's first use is still in flight (3.3, 3.5, 3.7, 7.4, 7.5): the acknowledgement of an order leaves before the store records the outcome, so a cancel or a status request another session of yours sends on the first 150=0 of its drop copy used to answer 35=9 102=1 This order was not found. 9110=4004 for an order that was live; it is now honoured from the moment our record exists, and a reference reused after the 24 hours never names the earlier order while the new one is being placed. The 35=9 row reads "41 names no order of this client" (unknown to the store and to the record); section 5 says the cancel of another key's order works from the acknowledgement on; conformance step 35 is the two-session cancel. Grown in place by the router chunk 9 of the same audit (2026-09-12; docs/11 revision 11 grown in place, OpenAPI 2.8.0): FOUR corrections, none moving a tag, sentence or code. (B-361, B-390, a behaviour correction, the documented contract restored) your session receives reports from the Logon reply on: the 35=A is always the first frame of the session with MsgSeqNum(34)=1, and a report built while your Logon is still being answered is lost for that session like one built with no live session (section 6, conformance step 1); a fill that landed during the Logon used to take 34=1 ahead of the reply and a standard engine dropped the session. (B-362) a connection from an address on NO partner's allowlist is closed at accept with no FIX frame, before it takes a pre-logon slot and before the throttle sees it, so a stranger cannot hold the slots against you; an IPv6 peer counts as its /64 for the per-address ceiling (section 1). (B-373) GET /partner/v1/me fix.enabled means our acceptor is LISTENING on the host you reached and reads false with fix.port null while a start failure of ours is being retried (section 1, conformance step 12). (B-389) a partnerClientRef never starts with BARR-, our own client uid namespace: POST /partner/v1/clients refuses it with the field sentence of section 2, the rule of 3.1 and 7.1 extended to the client reference. |
| 6 | 2026-09-10 | Grown in place by the partner settlement tranche of the same batch (BROKER-103; docs/11 revision 10 grown in place, OpenAPI 2.7.0): a 35=D of a client whose partner is close-only under the settlement tier answers the gateway-built 150=8 with 9110=4041 and the sentence in Text(58), 103=99, no 9118 (the 3.4 table), a 35=R of an opening request 297=5 9110=4041 and the 35=AJ of an opening request a 35=j with 9110=4041 (section 7; a close request and its accept never), the 9110 rows of 4.1 and 7.2 and Appendix A list the code. Grown in place by the barrier commission decisions of the same batch (OQ-36 D16, BROKER-8, BROKER-14; docs/11 revision 10 grown in place): Commission(12) of the early-close trade report carries our early-close commission (the client's BARRIER scheme on the premium at the accepted close price) instead of 0, and the opening commission of Commission(12) / 9153 is basis points of the premium with a per-contract minimum. The TERMS re-consent gate, for docs/11 revision 10 (the consents piece of the operator decision batch, BROKER-60): a new code 4035 in 9110 on a 150=8 refusal of a 35=D (section 3.4: 103=99, Text(58) the sentence naming where to accept, no 9118), on the 297=5 of an opening 35=R (7.1) and on the 35=j 380=0 of a 35=AJ accepting a quote of an OPENING request (7.4); the close request and the close accept, a reduce-only or reducing order are never gated; the gate is a switch on our side, off until compliance turns it on, so nothing changes for a partner until then; the acceptance is recorded over REST (POST .../consents, docs/11 section 10.5) and the notice arrives as the webhook client.consent_required, never over FIX. Appendix A's 9110 Code row and the 4.1 and 7.2 tables name the code. Grown in place by the KID documents tranche of the same batch (BROKER-95, BROKER-96; docs/11 revision 10 and OpenAPI 2.7.0 grown in place): two more refusals of an opening 35=R ride the same 297=5 9110=4035 with their sentence in Text(58) and no 9118 (7.1): the key information document gate (a RETAIL client's opening request without the acknowledged current KID of the market and variant, refused while none is published; the accept, the cancel and the close request never) and the strict consent gate (a strict take profit request while the client has not accepted the current Risk Disclosure version); the acknowledgement is recorded over REST (POST .../consents, docs/11 10.5); the 9132 row names the binding 8 h strict expiry; no new tag. Documentation only, the same revision (BROKER-86, the barrier listing facts of the same day): an omitted 9133 ExpiryTs (7.1) gets the open-end default capped at the venue's published maximum contract life less 60 s, the facts published on GET /market-info as barrierExpiry (docs/11 revision 10); no tag, sentence or code moved. Documentation only, the same revision (BROKER-23, the close-out follow-ups of the same batch, docs/11 revision 10 grown in place): 9121=CLOSE_OUT (4.1, section 5) also marks the forced sale of a client's spot holdings after a close-out (a SELL MARKET IOC on the spot market in the client's name, reported on the drop-copy session like a flatten; a production measure gated on the client agreement); no tag, sentence or code moved. The connection facts, documentation only, the same revision, for docs/11 revision 10, no wire change (BROKER-90): the development endpoint is dev.brokerage.perpetuals.com:9850, the same hostname as the Partner API, served with that hostname's own publicly trusted certificate (previously barriers.dev.mtf.perpetuals.com:9850, the router's certificate); no partner was live on FIX under the previous name, so nothing else changes: the port, the allowlist rule, every tag and sentence stay. Production connectivity stays confirmed at onboarding. Documentation only, the same revision (code audit 2026-09-11, B-050 and B-011): conformance step 29 names our early-close commission in 12 of the early-close trade (the step had shown a zero commission there since revision 3; the 7.6 table and this row were already right), and the 9121=CLOSE_OUT sentences of 4.1 and section 5 state the per-client rule of the forced sale (it runs once the client accepted the Terms version carrying the right-of-sale clause) in place of a gate on the client agreement; no tag, sentence or code moved. Documentation only, no wire change, the same revision (the code audit of 2026-09-11, B-053): the 9132 StrictTakeProfit row of 7.1 now states the strict consent gate for EVERY client whatever its categorisation and the KID gate for a RETAIL client only, as the gateway, docs/11 section 4, docs/12 7.2 and 7.4 and the OpenAPI codes sentence always did (the row had read the Risk Disclosure half as a RETAIL rule); no tag, sentence or code moved. Documentation only, no wire change, the same revision (the code audit of 2026-09-11, B-054, 2026-09-12): the 7.6 and 7.7 example frames carry the worked contract's opening commission on the PREMIUM basis of OQ-36 D16, 12=22.243500 and 9153=22.243500 on the premium 9152=2224.350000 (100 bps of the premium, the figure docs/12 section 7 shows for the same contract), in place of the pre-D16 notional-basis 13.09 they had kept; the frames' 9 and 10 recomputed by the build script, fix.html rebuilt, PartnerFixDocTests pins the figures against docs/12. Documentation only, no wire change, the same revision (the code audit of 2026-09-11, B-382 and B-403, 2026-09-12): Appendix A's list of standard tags written outbound names the session layer's own answers, 98, 108 and 141 on the Logon reply, 112 on the Heartbeat answering a TestRequest and 36, 123 on the GapFill SequenceReset (section 1 and conformance steps 1, 2 and 6 always expected them; the list had carried them inbound only, so a data dictionary built from it flagged the router's own session answers); no tag, sentence or code moved; fix.html rebuilt, PartnerFixDocTests pins the list against the session's three send sites. |
| 5 | 2026-09-08 | The review corrections of the same day, for docs/11 revision 9. Session layer (section 1): the logon throttle counts failed Logons per (key prefix, address) pair, so an address that is not yours can lock only itself out, an IPv6 peer counts as its /64, and a malformed Username(553) counts on the address alone; the credential re-check every 30 s also ends a live session whose address left your allowlist (Sign in again. 9110=4010); the message rate limits are stated (the REST buckets, shared, a 35=3 never metered); FIX tag values are ASCII (a byte above 0x7F answers Reject(35=3) 373=6 naming the tag, section 1 and 8.2, conformance step 32); the logon deadline runs from the TCP accept as always documented (it used to run from the completed TLS handshake); a live key logged on again at the partner ceiling hears the ceiling sentence, the documented order of the checks. Client attribution (section 2): the Parties echo rule is stated as built (a gateway-built report echoes what you sent, a hook-built report carries the client's partnerClientRef else the uid); partnerClientRef is printable ASCII at onboarding and the router strips the SOH delimiter from any value it frames, so no stored text can inject a tag. Orders (sections 3 to 5): a 150=0 that lands after a fill or a cancel was folded restates the record's 39, 14 and 151 instead of regressing the order to New (3.2, 4.2, step 34); OrigClOrdID(41) resolves through the order record past the store's 24 hours, so a resting order stays cancellable by your reference (3.3, 3.5, 3.7); the key that cancels an order it did not place receives that order's reports from the cancel on (3.5, section 5, step 33); the dedupe window is keyed on (session, message type, source_ref) (section 6). RFQ (section 7): the creation-time 35=AI is sent only while the request is still OPEN at the read-back, and a 35=AI without 9115 never supersedes one with it (7.2); Symbol(55) on a close request must match the contract's market (7.1); 9161 and 9162 ride on the early-close trade too (7.6, the presence cells); 131 on a 35=AJ or 35=Z resolves past the store like 41 (7.4, 7.5); the example frames of 7.2 to 7.4 are stamped in time order. |
| 4 | 2026-09-08 | The documentation revision, for docs/11 revision 9, no wire change: the four outbound tag tables (4.1, 7.2, 7.3 and the one table of the three contract reports in 7.6, which 7.7 now reads by presence) and the custom tag dictionary (Appendix A) are generated from the router's registries, one row per tag in the code's order, and pinned by our tests; every example frame carries a real BodyLength(9), CheckSum(10) and SendingTime(52) computed over its bytes, so the examples serve as test vectors (conformance step 31); the Logon example shows a key of the real shape; this document is served at /fix on the brokerage edge beside the Partner API reference at /docs. |
| 3 | 2026-09-08 | The barrier RFQ vocabulary, for docs/11 revision 9 (section 7): QuoteRequest(35=R) over the same function and sentences as POST .../rfq (7.1: the barrier terms in 9130 to 9136, the window in 9136 or 62, 9134 auto-accept by default) and, with 9155 CloseContractId, the early close by RFQ, QuoteReqID(131) as the request's Idempotency-Key (7.1.1: the identical replay as the current state with 9117=Y, the different payload as 297=5 9110=4092, the in-flight wait); QuoteStatusReport(35=AI) at creation and at every state change with the six QuoteStatus(297) values chosen (7.2, 7.9: 16 active, 10 pending, 0 accepted, 7 expired, 17 canceled, 5 rejected) and 9160 the router's own state; Quote(35=S) once per live quote id with 133 or 132 by the request's side, the premium, the acceptability verdict and its sentence, 9148 on a close (7.3); QuoteResponse(35=AJ) over the accept function with its refusals as 35=j 380=0 and the QuoteRespID(693) replay (7.4); QuoteCancel(35=Z) with 298=5 cancelling the whole request (7.5); the contract open and the early-close trade as 35=8 150=F 39=2 on the KO symbol with 9150 to 9154 and the terms (7.6), the contract end for any reason with 9156 to 9159 and the closing side (7.7), routed to the opening key, the closing key and the drop copies; the RFQ rows of 8.2, the conformance steps 23 to 30, the RFQ tags of Appendix A now read and written. The request records of docs/11 12.2 gain channel, channelKeyId and channelRef. |
| 2 | 2026-09-08 | Order entry, the ExecutionReport stream and the drop copy, for docs/11 revision 9: NewOrderSingle(35=D) over the same pipeline and sentences as the REST order route (section 3.1: the five named-on-absence tags, the types and times in force, 9101 to 9104, post-only, reduce-only, the record's channel, channelKeyId and channelRef), ClOrdID(11) as the order's Idempotency-Key (3.3: the identical replay as 150=I 9117=Y, the different payload as 150=8 103=6, the in-flight wait), the gateway-built refusal with the sentence verbatim and the 103 classification (3.4), OrderCancelRequest(35=F) and its 35=9 table (3.5), OrderStatusRequest(35=H) and the unknown-order status (3.7), the ExecutionReport tag table, the transitions and the 150=I rule (section 4, 9115 = the webhook's source_ref, 9111 = the trade id, the lost-acknowledgement rule), the routing of reports keyed on the session's key and the drop copy with 9121 (section 5), the delivery semantics (section 6: in memory, after the commit, lost without a session, the dedupe window), the order rows of 8.2, the conformance steps 14 to 22. The RFQ vocabulary (section 7) arrives with the next revision; 35=R, 35=AJ and 35=Z still answer 35=j 380=4 9110=5030. |
| 1 | 2026-09-08 | First revision, for docs/11 revision 9: the session layer (section 1: the endpoint and the ceilings, the CompIDs, the Logon over the partner key with every refusal's sentence and code, the logon throttle, one session per key with the replacement after two missed heartbeats, the credential re-check, no message store and the GapFill, heartbeats and TestRequest, the zero-length rule, restarts, the operations disconnect, the Logout texts, GET /me fix), the client attribution (section 2), the Cancel/Replace refusal and the 35=9 sentinels (section 3), the drop-copy session's establishment and its session-level refusal (section 5), the delivery posture (section 6), the reject doctrine and the reply policy (section 8), what FIX does not carry (section 9), the session steps of the conformance checklist (section 10) and the custom tag dictionary (Appendix A). Order entry, the ExecutionReport stream, the routing of reports and the RFQ vocabulary are specified by the design addendum and arrive with the next revisions; until then those messages answer 35=j 380=4 9110=5030. |