Dictionaries

The engine is dictionary-driven: it knows nothing about FIX 4.4 or 5.0 SP2 until you hand it a dictionary package. Each package is pure data — fields, messages, components, datatypes, enums — generated from permissively-licensed sources, cross-checked in CI, and shipped with the Tags, MsgType and typed-message exports the other guides use.

Which package?

Match the counterparty. @boarteam/fix-dict-fix44 (FIX 4.4) is the workhorse of FX and CFD connectivity; @boarteam/fix-dict-fix42 covers the older venues that never left 4.2; @boarteam/fix-dict-fix50sp2 is the self-contained FIX 5.0 SP2 dictionary — envelope, session and application messages in one. Read the version off the wire: BeginString (8) says FIX.4.4 or FIX.4.2 directly, while FIX 5.0 SP2 messages say FIXT.1.1 — the transport FIX 5.0 introduced — with the application version riding in ApplVerID (1128) / DefaultApplVerID (1137).

dictionaries-choose.mtsts
import { createFixEngine, tokenize, type FixEngine } from '@boarteam/fix';import { dictionary as fix44 } from '@boarteam/fix-dict-fix44';import { dictionary as fix42 } from '@boarteam/fix-dict-fix42';import { dictionary as fix50sp2 } from '@boarteam/fix-dict-fix50sp2';// One engine per dictionary, chosen by the message's own BeginString (8).// FIX 5.0 SP2 rides the FIXT.1.1 transport, so that is the BeginString its// messages carry on the wire.const engines: Record<string, FixEngine> = {  'FIX.4.4': createFixEngine(fix44),  'FIX.4.2': createFixEngine(fix42),  'FIXT.1.1': createFixEngine(fix50sp2),};const line =  '8=FIXT.1.1|9=74|35=A|49=SENDER|56=TARGET|34=1|52=20240101-12:00:00.000|98=0|108=30|1137=9|10=197|';// tokenize is the cheap way to peek: tag/value pairs, no dictionary needed.const [[, beginString]] = tokenize(line, { soh: '|' });console.log(beginString); // → FIXT.1.1const fix = engines[beginString];const { message } = fix.parse(line, { soh: '|' });console.log(message.name); // → Logon// DefaultApplVerID (1137) names the application version — "9" is FIX 5.0 SP2:console.log(message.fields[1137].value); // → 9

What each dictionary contains — counts, provenance, recorded gaps — is documented on coverage & correctness, and the versions pages compare the dialects field by field.

The FIXT.1.1 transport pair

FIX 5.0 split the protocol into a transport layer (FIXT.1.1: the envelope and session messages) and an application layer. For most decoding, the self-contained fix-dict-fix50sp2 is all you need. The fourth package, @boarteam/fix-dict-fixt11, is the transport-only dictionary for the pair API: hand createFixEngine a { transport, app } pair and every validation finding is attributed to its layer — which is the difference between answering with a session Reject (3) and a BusinessMessageReject (j).

dictionaries-fixt-pair.mtsts
import { createFixEngine } from '@boarteam/fix';import { dictionary as fixt11 } from '@boarteam/fix-dict-fixt11';import { dictionary as fix50sp2 } from '@boarteam/fix-dict-fix50sp2';// The FIXT pair: the transport dictionary owns the envelope and session// messages, the application dictionary owns the business messages. Every// validation finding is then attributed to its layer.const fix = createFixEngine({ transport: fixt11, app: fix50sp2 });// A NewOrderSingle missing BOTH its sequence number (session) and its// OrdType (application):const raw =  '8=FIXT.1.1|9=115|35=D|49=BUYSIDE|56=SELLSIDE|52=20240101-12:00:00.000|' +  '11=ORDER-9|55=EUR/USD|54=1|60=20240101-12:00:00.000|38=250000|10=168|';const { message, issues } = fix.parse(raw, { soh: '|' });console.log(issues.length); // → 0// `layer` tells you which side of the split each problem belongs to — i.e.// whether to answer with a session Reject (3) or a BusinessMessageReject (j):for (const issue of fix.validate(message)) {  console.log(issue.layer, issue.code, issue.path);}// → session validate/required-field-missing MsgSeqNum// → application validate/required-field-missing OrdType

This reference’s browsable pages cover FIX 4.4, 4.2 and 5.0 SP2; FIXT.1.1 is deliberately not a fourth set of pages, because its 74 fields are a subset of the FIX 5.0 SP2 dictionary’s — as a package it exists precisely for the pair API above.

Skipping the engine: free functions

createFixEngine is a thin convenience that pre-binds the dictionary. Every capability is also exported as a free function — parse, parseAll, validate, encode, tokenize, splitMessages, decodeValue, calculateChecksum, bodyLength, loadDictionary, validateDictionary, the typed-message helpers and the extension helpers — for callers who prefer to pass the dictionary explicitly.

dictionaries-free-functions.mtsts
import { loadDictionary, parse, splitMessages, validate } from '@boarteam/fix';import { dictionary as fix44Json } from '@boarteam/fix-dict-fix44';// No engine required. The dict packages ship plain JSON; loadDictionary// builds the indexed runtime form once, and the free functions take it// explicitly — same behaviour, same output shapes as the engine methods.const dict = loadDictionary(fix44Json);const raw =  '8=FIX.4.4|9=130|35=W|49=SENDER|56=TARGET|34=2|52=20240101-12:00:00.000|' +  '55=EUR/USD|268=2|269=0|270=1.0921|271=1000000|269=1|270=1.0923|271=1000000|10=248';const { message, issues } = parse(raw, dict, { soh: '|' });console.log(message.name, issues.length); // → MarketDataSnapshotFullRefresh 0console.log(validate(message, dict).length); // → 0// splitMessages frames a concatenated capture without decoding it:const log =  '8=FIX.4.4|9=68|35=A|49=BUYSIDE|56=BROKER|34=1|52=20240101-12:00:00.000|98=0|108=30|10=014|' +  '8=FIX.4.4|9=56|35=0|49=BUYSIDE|56=BROKER|34=2|52=20240101-12:00:01.000|10=237|';console.log(splitMessages(log, { soh: '|' }).length); // → 2

Output shapes

Four structures cover everything the engine returns; all four are part of the SemVer contract.

TypeShapeNotes
ParsedMessage{ msgType, name?, beginString?, framed, fields, groups }fields and groups are keyed by tag; framed only means the envelope was found.
ParsedField{ tag, name?, raw, value }raw is the verbatim wire text (the source of truth for re-encoding); value is typed by the datatype.
ParsedGroupEntry{ fields, groups }Groups are arrays of these, addressed by counter tag (message.groups[268]) — never parallel arrays.
FixIssue{ code, severity, message, path?, refTagID?, refSeqNum?, refMsgType?, sessionRejectReason?, layer? }code is stable under SemVer; layer appears when validating against a transport/application pair.