Protocols & transports
Every margin service is reachable over two wire protocols on the same URL:
| Protocol | Content-Type | Best for |
|---|---|---|
| gRPC (binary, HTTP/2) | application/grpc | Native clients (Rust, Go, Python, …) — the hot path. |
| gRPC-Web (binary) | application/grpc-web+proto | Browsers and other HTTP/1.1 clients. |
The server is tonic with tonic_web, so gRPC-Web is translated in
process — there is no separate proxy. CORS allows any origin, method,
and header, and exposes response headers, so browser apps can call the
public host directly. There is no JSON / Connect-protocol endpoint;
the OpenAPI file describes the gRPC-Web URLs for tooling, not a REST
API.
The Flint SDKs use:
| SDK | Default transport |
|---|---|
Rust (flint-margin-user-client, flint-margin-maker-client) | gRPC over HTTPS (HTTP/2) |
TypeScript (@superis-labs/flint-margin-user-client, @superis-labs/flint-margin-maker-client) | gRPC-Web binary over HTTPS |
The one bidirectional RPC, the maker SessionService.Session
heartbeat, needs HTTP/2 gRPC; gRPC-Web can't carry it. Everything
else works on either transport.
Request headers
| Header | Sent by | Meaning |
|---|---|---|
authorization: Bearer <session_token> | You / SDK AuthFlow | Required on every maker service except AuthService and NodeService. Ignored by the public user services. |
x-api-key: <key> | You | Alternative credential for MakerService and EscalationService, for organizations with perps access. |
x-flint-sdk | SDK | SDK identity: rust/<version> or ts-web/<version>. |
x-flint-proto-hash | SDK | Fingerprint of the proto tree the SDK was generated from. |
x-flint-proto-rev | SDK | flint-protos git revision the SDK was generated from. |
grpc-timeout | You / SDK | Client deadline, standard gRPC encoding (e.g. 5000m = 5 s). |
The x-flint-* identity headers are diagnostics: the server logs them
alongside each request so support can tell which SDK build made a
call. They do not gate compatibility today. Hand-rolled clients may
omit them.
Response headers
Every response — including rate-limit rejections — carries the server's identity in its headers (and, for gRPC, its trailers):
| Header | Meaning |
|---|---|
x-flint-server-version | Server release version. |
x-flint-server-commit | Server git commit. |
x-flint-server-proto-hash | Proto fingerprint the server was built from. |
x-flint-server-proto-rev | Proto revision the server was built from. |
Comparing x-flint-server-proto-hash with the SDK's own PROTO_HASH
constant is the quickest way to confirm a client and server agree on
the schema. In TypeScript, responseMetadata(err) and
serverMetadataFromHeaders(headers) read these for you.
Deadlines and timeouts
- Server side. The server bounds how long it spends producing a
response and returns
DEADLINE_EXCEEDEDwhen that limit (15 s by default) is hit. - Rust. Channels built by the SDK — including the ones endpoint
discovery dials — use a 5 s connect timeout and a 10 s per-request
timeout, with HTTP/2 keep-alive pings every 30 s (also while idle)
and TCP keep-alive every 60 s. Override per call with
tonic::Request::set_timeout. - TypeScript. No timeout by default. Pass
timeoutMson a call (sent asgrpc-timeout) orsignalfor cancellation; thecreateXxxClientfactories in the user SDK also take a client-widetimeoutMs, which applies to that client's streams as well.
Don't put a short deadline on a server stream: the deadline covers the whole call, so it would cut a healthy subscription.
Streaming
Every Subscribe* RPC is a long-lived server stream and is the
recommended path for anything event-driven:
MarketDataService.SubscribeOrderBook/SubscribeCandlesFundingService.SubscribeFundingRatesAccountService.SubscribeAccount/SubscribeAccounts/SubscribeFills/SubscribeActivityMarketService.SubscribeMarketsRiskService.SubscribeAccountsAtRiskMakerService.SubscribeMarketSnapshots/SubscribeFills/SubscribeQuotesTxService.SubscribeTxStatus/SubscribeBlockhash/SubscribeSlots/SubscribeLeadersSpreadProtectService.SubscribeSpreadProtectSessionService.Session(bidirectional heartbeat)
Things to know:
- Snapshot first. Most streams open with a full snapshot (flagged
snapshot = truewhere the message has the field), then send updates. On reconnect you get a fresh snapshot — replace local state rather than merging. - Order book sequencing.
SubscribeOrderBookmessages carrysequenceandprevious_sequence. Ifprevious_sequencedoesn't match the lastsequenceyou applied, drop the book and resubscribe. - Slow consumers are cut off. If you fall behind the server's
buffer, the stream ends with
DATA_LOSS(account activity, fills, candles) orRESOURCE_EXHAUSTED(accounts at risk). Reconnect and reconcile against the matching unary RPC —GetActivity,GetFills,GetCandles, orListAccountsAtRisk. - Live-only streams.
SubscribeActivityandSubscribeFillsdo not replay history. Backfill with the unary RPC after a gap. - Reconnect with backoff. The TypeScript SDKs export
streamWithBackoff, which re-opens a stream on a 500 ms → 30 s exponential schedule. The Rust maker client'ssubscribe_*methods return aResilientStreamthat does the same, re-authenticating as needed. With a bare generated client, loop over the stream and reopen on error.
Each open stream holds one of your 100 concurrent stream slots until it closes — see Rate limits.
gRPC examples
The SDKs are the supported clients. For quick checks from a shell,
grpcurl works against the bundled protos. These commands use the running
local fixture. For another network, use the operator's
host:port values and omit -plaintext when the gateway uses TLS. Use a margin
bearer session from that same gateway.
# Public — perp market catalog.
grpcurl -plaintext \
-proto margin-user.proto \
-d '{}' \
127.0.0.1:15061 \
flint.margin.user.v1.PerpMarketService/ListPerpMarkets
# Public — one trader account.
grpcurl -plaintext \
-proto margin-user.proto \
-d '{"trader_id":{"id":"42"}}' \
127.0.0.1:15061 \
flint.margin.user.v1.AccountService/GetAccount
# Authenticated — pass the session token as metadata.
grpcurl -plaintext \
-proto margin-maker.proto \
-H "authorization: Bearer ${FLINT_MARGIN_TOKEN}" \
-d '{}' \
127.0.0.1:15062 \
flint.margin.maker.v1.MakerService/GetQuotingState
# Public — this node's identity, program id, and health.
grpcurl -plaintext \
-proto margin-maker.proto \
-d '{}' \
127.0.0.1:15062 \
flint.margin.maker.v1.NodeService/GetNodeInfogrpcurl takes JSON for convenience; uint64 fields such as
trader_id.id are strings in proto JSON. The request still goes over
the wire as protobuf.
