> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cdp.coinbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Coinbase Derivatives: Predictions FIX Session Messages

The session layer authenticates your API key, keeps the connection alive, and keeps message sequence numbers in order on the order entry, market data, and drop copy gateways. Differences between the gateways are in **bold**.

> **Session Message Types**
>
> Client and server use the following session messages:
>
> * [Logon (35=A)](#logon-35a): Opens and authenticates a session.
> * [Logout (35=5)](#logout-355): Closes a session.
> * [Heartbeat (35=0)](#heartbeat-350): Keeps the session alive and answers a TestRequest.
> * [TestRequest (35=1)](#testrequest-351): Requests a Heartbeat.
> * [ResendRequest (35=2)](#resendrequest-352): Requests missed messages by sequence number.
> * [Reject (35=3)](#reject-353): Session-level reject of a message that failed session validation.
> * [SequenceReset (35=4)](#sequencereset-354): Gap-fills the sequence number.

> **Fill Recovery Message Types**
>
> **Order entry and drop copy only.** Use these application messages to recover missed fills:
>
> * [LastExecIDRequest (35=F1)](#lastexecidrequest-35f1): Client request for the latest `ExecID`.
> * [LastExecID (35=F2)](#lastexecid-35f2): System response to F1.
> * [EventResendRequest (35=F3)](#eventresendrequest-35f3): Client request to replay fills in an `ExecID` range.
> * [EventResendComplete (35=F4)](#eventresendcomplete-35f4): System response to a successful F3 request.
> * [EventResendReject (35=F5)](#eventresendreject-35f5): System response to an unsuccessful F3 request.

## Gateway comparison

| Behavior | Order Entry | Market Data | Drop Copy |
| :- | :- | :- | :- |
| TargetCompID (56) | **`CPMOE`** | **`CPMMD`** | **`CBPMDC`** |
| Required permission | **`TRADE`** | **`MARKET_DATA`** | **`DROP_COPY`** |
| Sessions per API key | **One** | **Several**, with `<apiKey>.<tag>` | **One** |
| Maximum HeartBtInt (108) | **300 seconds** | **300 seconds** | **30 seconds** |
| SendingTime accuracy for application messages | **60 seconds** | **60 seconds** | **5 seconds** |
| ResendRequest (35=2) | **One gap fill. Nothing is retransmitted.** | **Retransmits application messages and gap-fills session messages.** | **One gap fill. Nothing is retransmitted.** |
| Fill recovery (35=F1 to 35=F5) | **Yes. 30-minute replay window, per API key.** | **No** | **Yes. 24-hour replay window, per scope.** |
| Cancel on disconnect | **Yes**, opt-in on Logon | **No** | **No** |
| MarketSettlementReport (35=UMS) | **Opt-in on Logon** | **Not sent** | **Always sent** |

## Logon (35=A)

The Logon message opens a session. The gateway authenticates the API key in SenderCompID (49), the passphrase, and an HMAC signature, then confirms with a Logon that carries the heartbeat interval in use. If authentication fails, the gateway rejects the Logon and ends the session.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 98 | EncryptMethod | Int | Y | `0` = None. |
| 108 | HeartBtInt | Int | Y | Heartbeat interval in seconds. `30` is recommended. `0` uses 10 seconds. Larger values are capped at **Order entry: 300 seconds**; **Market data: 300 seconds**; **Drop copy: 30 seconds**. Both sides use the resulting value. |
| 141 | ResetSeqNumFlag | Boolean | N | `Y` = Reset sequence numbers and start a new session. `N` = Keep previous sequence numbers. |
| 789 | NextExpectedMsgSeqNum | SeqNum | N | Accepted and ignored. |
| 554 | Password | String | Y | Your API passphrase. Must not be empty. |
| 58 | Text | String | C | Logon signature. See [Logon signature](#logon-signature). Must be exactly 44 characters of standard padded Base64 that decodes to 32 bytes. Required unless you send `RawData (96)`. If both are sent, `Text (58)` is used. |
| 96 | RawData | String | C | Alternative field for the Logon signature, read only when `Text (58)` is absent. Same format as `Text (58)`. |
| 8013 | CancelOrdersOnDisconnect | Char | N | **Order entry only.** Cancel-on-disconnect scope for this session. `N` = No cancel (default). `Y` = Cancel this session's orders. `A` = Cancel all orders of the API key. See [Cancel on disconnect](#cancel-on-disconnect). |
| 20127 | ReceiveSettlementReports | Boolean | N | **Order entry only.** `Y` = Receive [MarketSettlementReport (35=UMS)](/derivatives-predictions/fix/order-entry.md#marketsettlementreport-35ums) messages. `N` = Do not receive them (default). Drop copy sends settlement reports without opt-in. |
| 1137 | DefaultApplVerID | String | Y | `9` = FIX 5.0 SP2. |

The API key is carried in the header field SenderCompID (49), not in a Logon body field. To reset sequence numbers, send `ResetSeqNumFlag (141) = Y` on Logon.

### Logon signature

Compute the signature as follows:

1. Build the payload by concatenating, without delimiters: `SendingTime + SenderCompID + TargetCompID + Passphrase`.
   * `SendingTime` is the Logon's SendingTime (52) formatted as a UTC timestamp with millisecond precision: `YYYYMMDD-HH:MM:SS.sss`. If you send SendingTime (52) with finer precision, sign the value truncated to milliseconds.
   * `SenderCompID` is the exact value of tag 49: your API key. **Market data only:** include the `.<tag>` suffix when you send a tagged SenderCompID.
   * `TargetCompID` is **Order entry:** `CPMOE`; **Market data:** `CPMMD`; **Drop copy:** `CBPMDC`.
   * `Passphrase` is the value of Password (554).
2. Encode the payload as UTF-8.
3. Decode your API secret with standard Base64.
4. Compute HMAC-SHA256 over the payload bytes, keyed with the decoded secret.
5. Encode the 32-byte result with standard padded Base64 (exactly 44 characters) and send it in `Text (58)`.

> [!NOTE]
>
> SendingTime (52) on the Logon must be within 30 seconds of the gateway's clock, in either direction. A Logon outside this window is rejected.

The Logon is accepted only when all of the following are true:

* The API key exists, and the passphrase and signature are valid for it.
* The API key's user is active and has the gateway's permission: **Order entry:** `TRADE`; **Market data:** `MARKET_DATA`; **Drop copy:** `DROP_COPY`.
* The API key's firm or FCM scope resolves. For a firm-level key, both the firm and its FCM must be active. For an FCM-level key, the FCM must be active.
* **Market data only:** the API key has not reached its maximum number of concurrent sessions.

### Sessions per API key

**Order entry and drop copy:** one session per API key. A new Logon with the same API key replaces the existing session, which receives Logout (35=5) with `Text (58) = replaced by newer connection`. Tagged SenderCompIDs such as `apiKey.01` are not supported. On order entry, a replaced session does not trigger [cancel on disconnect](#cancel-on-disconnect).

### Multiple sessions per API key

**Market data only.** You can open several concurrent sessions with one API key by appending a session tag to the key in SenderCompID (49):

* Format: `<apiKey>.<tag>`, where the API key is the full 16-character key, the delimiter is `.`, and the tag is a two-digit index from `01` to `99`. Example: `AB12CD34EF56GH78.01`.
* Each distinct `<apiKey>.<tag>` is an independent session with its own sequence numbers.
* Logging on again with the same `<apiKey>.<tag>` takes over that session. The replaced session receives Logout (35=5) with `Text (58) = replaced by newer connection`.
* A non-canonical tag is rejected at Logon, for example `1`, `00`, `01.2`, or an empty tag.
* A bare API key without a tag is also accepted and counts as one session.
* All sessions of one key share that key's `MARKET_DATA` permission.
* Each API key has a configured maximum number of concurrent sessions. A Logon that would exceed it is rejected.

> [!NOTE]
>
> Sign the full SenderCompID you send in tag 49, including the `.<tag>` suffix. A signature computed over the bare API key fails when the SenderCompID is tagged.

## Logout (35=5)

Closes a session. Either side can send it.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 58 | Text | String | N | Logout reason. |

## Heartbeat (35=0)

Keeps the session alive. Answers a TestRequest with the same `TestReqID (112)`.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 112 | TestReqID | String | C | Required when answering a TestRequest. Echoes its `TestReqID`. |

The gateway sends a Heartbeat when it has sent nothing for most of the heartbeat interval. If the gateway receives nothing from you for one interval, it sends a TestRequest. If it receives nothing for two intervals, it ends the session.

## TestRequest (35=1)

Requests a Heartbeat carrying the same `TestReqID (112)`. Either side can send it.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 112 | TestReqID | String | Y | Identifier the recipient echoes in its Heartbeat. |

## ResendRequest (35=2)

Requests messages `BeginSeqNo (7)` to `EndSeqNo (16)`.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 7 | BeginSeqNo | SeqNum | Y | First sequence number of the range. |
| 16 | EndSeqNo | SeqNum | Y | Last sequence number of the range. `0` = through the latest message. |

The gateways answer a ResendRequest differently.

### Order entry and drop copy gap fill

**Order entry and drop copy only.** The gateway answers with one SequenceReset (35=4) gap fill and does not retransmit any messages:

| Tag | Name | Value |
| :- | :- | :- |
| 34 | MsgSeqNum | Your `BeginSeqNo (7)`. |
| 43 | PossDupFlag | `Y` |
| 52 | SendingTime | Current UTC time. Precision: **Order entry:** nanoseconds; **Drop copy:** milliseconds. |
| 122 | OrigSendingTime | Same as `SendingTime (52)`. |
| 123 | GapFillFlag | `Y` |
| 36 | NewSeqNo | `EndSeqNo (16)` + 1. When `EndSeqNo (16) = 0`, the gateway's next outbound sequence number. |

An invalid ResendRequest is ignored. No SequenceReset or reject is sent.

> [!NOTE]
>
> A ResendRequest never returns execution reports or settlement reports on order entry or drop copy. To recover missed fills, use [LastExecIDRequest (35=F1)](#lastexecidrequest-35f1) and [EventResendRequest (35=F3)](#eventresendrequest-35f3). Other order entry reports (new, cancel, reject, mass cancel, settlement) cannot be recovered after a disconnect.

### Market data retransmission

**Market data only.** The gateway resends the requested range from its record of messages sent on your session:

* Application messages are retransmitted with their original `MsgSeqNum (34)`, `PossDupFlag (43) = Y`, `OrigSendingTime (122)` set to the original `SendingTime (52)`, and a new `SendingTime (52)`.
* Session messages in the range are replaced by a SequenceReset (35=4) with `GapFillFlag (123) = Y` and `PossDupFlag (43) = Y`.
* An `EndSeqNo (16)` of `0`, or beyond the last message sent, means through the last message sent.
* A `BeginSeqNo (7)` at or beyond the gateway's next outbound sequence number is rejected with Reject (35=3), `SessionRejectReason (373) = 5`, `Text (58) = invalid resend requests`.
* An `EndSeqNo (16)` less than `BeginSeqNo (7)` is rejected with Reject (35=3), `SessionRejectReason (373) = 5`, `Text (58) = endSeqNo is less than beginSeqNo`.
* Messages that are no longer retained are not resent.

## Reject (35=3)

Session-level reject of a message that failed session validation. Either side can send it.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 45 | RefSeqNum | SeqNum | Y | `MsgSeqNum (34)` of the rejected message. |
| 372 | RefMsgType | String | N | `MsgType (35)` of the rejected message. |
| 373 | SessionRejectReason | Int | N | FIX session reject reason code, for example `5` = Value is incorrect, `9` = CompID problem, `10` = SendingTime accuracy problem. |
| 58 | Text | String | N | Reject reason. |

### SendingTime accuracy

An application message whose SendingTime (52) differs from the gateway's clock by more than the limit is rejected with Reject (35=3) and `SessionRejectReason (373) = 10` (SendingTime accuracy problem). The limit is **Order entry: 60 seconds**; **Market data: 60 seconds**; **Drop copy: 5 seconds**. Session messages are not checked.

## SequenceReset (35=4)

Resets or gap-fills the sequence number to `NewSeqNo (36)`. Either side can send it.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 123 | GapFillFlag | Boolean | N | `Y` = Gap fill. `N` or omitted = Reset. The SequenceReset the gateway sends in answer to a ResendRequest always uses `Y`. |
| 36 | NewSeqNo | SeqNum | Y | Next sequence number. |

## Fill recovery

**Order entry and drop copy only.** After a reconnect, use [LastExecIDRequest (35=F1)](#lastexecidrequest-35f1) to check whether you missed fills, then [EventResendRequest (35=F3)](#eventresendrequest-35f3) to replay them. Market data has no fill recovery.

| Property | Order Entry | Drop Copy |
| :- | :- | :- |
| Fills covered | **Fills of your API key.** | **Fills for your [scope](/derivatives-predictions/fix/drop-copy.md#subscription-scope)** (firm or FCM), across all sessions in that scope. |
| Replay window | **30 minutes** | **24 hours** |
| Concurrent replays | **One per API key.** | **One per scope, across all sessions in that scope.** |
| Value returned by LastExecID (35=F2) | **`ExecID` of your newest fill.** | **Last `ExecID` sent for your scope, for a report of any type.** |

`ExecID (17)` values increase over time. Compare them numerically. Values are not consecutive.

### LastExecIDRequest (35=F1)

Requests the latest `ExecID`: **Order entry:** of your newest fill; **Drop copy:** sent for your [scope](/derivatives-predictions/fix/drop-copy.md#subscription-scope). The request has no age limit. The response is always [LastExecID (35=F2)](#lastexecid-35f2).

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 112 | TestReqID | String | N | Accepted and ignored. Not echoed. Correlate the response with `RefSeqNum (45)` on LastExecID (35=F2). |

### LastExecID (35=F2)

Sent in response to LastExecIDRequest.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 45 | RefSeqNum | SeqNum | Y | `MsgSeqNum (34)` of the request. |
| 17 | ExecID | String | Y | **Order entry:** `ExecID` of your newest fill (`150=F`). `0` when you have no fills. **Drop copy:** last `ExecID` sent for your scope, for a report of any type, including `MarketSettlementReportID (20105)`. `0` if none exists. |

* **Order entry:** if this `ExecID` is greater than the last `ExecID` you processed, you missed fills. The `ExecID` can be older than the replay window of [EventResendRequest (35=F3)](#eventresendrequest-35f3). A fill reported here is not necessarily replayable.
* **Drop copy:** when no recent report is available, the gateway returns the `ExecID` of the newest stored fill for your scope, however old. The value marks a position in the `ExecID` sequence. It can be the `ExecID` of a report that is not a fill, or of the other side of a fill. Use it as a lower bound for `BeginExecId (22003)`, not as proof that a fill can be resent.

### EventResendRequest (35=F3)

Requests replay of fills (`150=F`) in an `ExecID` range: **Order entry:** fills of your API key; **Drop copy:** fills for your [scope](/derivatives-predictions/fix/drop-copy.md#subscription-scope). Replayed fills are sent as ExecutionReports in ascending `ExecID` order, followed by [EventResendComplete (35=F4)](#eventresendcomplete-35f4) or [EventResendReject (35=F5)](#eventresendreject-35f5).

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 22003 | BeginExecId | String | Y | Lower bound (inclusive), a positive decimal integer. Need not equal a stored `ExecID`; replay starts at the next fill. Must not be before the oldest fill in the replay window. |
| 22004 | EndExecId | String | N | Upper bound (inclusive). Omit to replay through the newest fill. Must not be less than `BeginExecId` or later than the newest fill. |

> [!NOTE]
>
> Only fills are replayed: ExecutionReports with `150=F`. New, pending new, canceled, expired, and rejected reports, cancel-on-disconnect reports, OrderCancelReject, OrderMassCancelReport, and MarketSettlementReport are not replayed. The replay window is **Order entry: 30 minutes**; **Drop copy: 24 hours**. Fills older than the window are not replayed. A `BeginExecId` before the oldest fill in the window is rejected with `22006=1`; it is not moved forward to that fill, and no later fills are sent.

* Replayed fills carry `PossResend (97) = Y`. `PossDupFlag (43)` is not set. Use `97`, not `TransactTime`, to tell replayed reports from live ones.
* Replayed fills have new `MsgSeqNum (34)` and `SendingTime (52)` values and keep their original `ExecID (17)` and `TransactTime (60)`.
  * **Order entry:** the report body is unchanged, including the `Symbol (55)` at the time of the trade.
  * **Drop copy:** order fields are unchanged. Parties (453) reflect firm, FCM, and user details at the time of the resend.
* One replay runs at a time: **Order entry:** per API key; **Drop copy:** per scope, across all sessions in that scope.
* If a replay fails after fills were sent, EventResendReject (35=F5) replaces EventResendComplete (35=F4). When a replayed fill cannot be delivered, the gateway stops and sends `22006=5`. Do not advance past the last fill you received; request the range again.
* **Order entry:** fills inside the window can still be unavailable for replay. Do not use Event Resend as a system of record. Request a replay promptly after you reconnect, starting from your last processed `ExecID`.
* **Order entry:** if your API key no longer has `TRADE` permission or its scope no longer resolves, the request is rejected with [BusinessMessageReject (35=j)](/derivatives-predictions/fix/order-entry.md#businessmessagereject-35j). **Drop copy:** the gateway ends a session whose authorization no longer holds; see [Drop Copy](/derivatives-predictions/fix/drop-copy.md#subscription-scope).

### EventResendComplete (35=F4)

Sent after all replayed fills of a successful request.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 45 | RefSeqNum | SeqNum | Y | `MsgSeqNum (34)` of the request. |
| 22005 | ResentEventCount | Int | Y | Number of fills replayed. |

### EventResendReject (35=F5)

Sent when an EventResendRequest cannot be completed. Except for some `22006=5` cases, no fills are sent before an EventResendReject.

| Tag | Name | FIX Type | Req | Description |
| :- | :- | :- | :- | :- |
| 45 | RefSeqNum | SeqNum | Y | `MsgSeqNum (34)` of the request. |
| 22006 | EventResendRejectReason | Int | Y | See the values below. |
| 58 | Text | String | N | Description. |

| EventResendRejectReason (22006) | Sent when |
| :- | :- |
| `1` = BEGIN\_EXEC\_ID\_TOO\_SMALL | The replay window holds no fills, `BeginExecId` is before the oldest fill in the window, or no fill falls in the requested range. **Order entry:** also when `BeginExecId` or `EndExecId` is not a valid integer. **Drop copy:** also when `BeginExecId` is `0` or negative. |
| `2` = END\_EXEC\_ID\_TOO\_LARGE | `BeginExecId` is after the newest fill in the window, `EndExecId` is after the newest fill, or `EndExecId` is less than `BeginExecId`. |
| `3` = RESEND\_ALREADY\_IN\_PROGRESS | A replay is already running (**Order entry:** for your API key; **Drop copy:** for your scope), or the gateway cannot accept more replays. Retry after the active replay completes. |
| `4` = TOO\_MANY\_RESEND\_REQUESTS | Defined but not currently sent. |
| `5` = SERVER\_ERROR | Replay failed, including a delivery failure partway through. When `Text (58) = replay sources do not meet`, the requested range spans a gap in retained fills and no fills are sent. **Drop copy:** also when `BeginExecId` or `EndExecId` is not a decimal integer, or the gateway could not read the requested fills. |

## Cancel on disconnect

**Order entry only.** Cancel on disconnect is set per session with `CancelOrdersOnDisconnect (8013)` on [Logon (35=A)](#logon-35a). The scope is fixed for the life of the session.

| CancelOrdersOnDisconnect (8013) | Effect when the session disconnects |
| :- | :- |
| `N` = No cancel (default) | Orders stay open. |
| `Y` = Cancel for session | This session's orders are canceled. |
| `A` = Cancel for API key | All open orders of the API key are canceled. |

* Orders are canceled only when the client disconnects. When a new Logon with the same API key replaces the session, no orders are canceled.
* Each canceled order is reported as [Canceled](/derivatives-predictions/fix/order-entry.md#canceled-358-1504) with `378=1` and `Text (58) = CLIENT_DISCONNECT`. These reports are delivered only to a session that is connected when the cancel happens, and are not replayed by EventResendRequest.
* Send the scope you want on every Logon.
