buf.build/monaco-sdk/api.
Endpoint
Protobuf Module
See the gRPC protobuf reference for the curated protobuf file, package, service, and message reference.
Services
AuthService
AuthService
Wallet authentication, session refresh, and session revocation.
OrdersService
OrdersService
Order entry, cancellation, replacement, batch operations, order lookup, and conditional TP/SL order management.
AccountsService
AccountsService
Profile, balances, ledger movements, funding payments, sub-accounts, user trades, and portfolio analytics.
MarketService
MarketService
Public market data, candles, screener data, perp market config, funding, open interest, mark price, and index price.
OrderbookService
OrderbookService
Public orderbook snapshots.
GetOrderbookRPC is a gRPC-only wrapper over the same response type.TradesService
TradesService
Public trade history, single-trade lookup, and authenticated user trade history.
MarginAccountsService
MarginAccountsService
Isolated-margin account summaries, collateral availability, collateral transfers, movements, and order-risk simulation.
PositionsService
PositionsService
Margin positions, close flows, risk checks, margin adjustments, position history, and TP/SL attachment.
ApplicationsService
ApplicationsService
Application configuration (session-authenticated) and public,
client_id-selected users, balances, orders, movements, and stats.DelegatedAgentsService
DelegatedAgentsService
Delegated-agent registration, owner lookup, revocation, and delegated-session creation.
WithdrawalsService
WithdrawalsService
Withdrawal initiation, pending-withdrawal listing, and signed calldata lookup.
FeesService
FeesService
Fee simulation for prospective orders.
FaucetService
FaucetService
Testnet token minting for authenticated users.
WhitelistService
WhitelistService
Public whitelist applications are closed.
SubmitWhitelist returns PERMISSION_DENIED without changing applicant data.HealthService
HealthService
Public service health check.
Generate a Client
Authenticate requests
Monaco is tokenless. There is no access token to attach: you generate an ed25519 session keypair locally, register its public key during challenge/verify, and sign every authenticated request with the session private key. The server verifies the signature against the stored public key (grpc-api/src/auth/interceptor.rs); public market-data RPCs need no signature.
grpcurl cannot authenticate: the signature is taken over the
prost-encoded request body, which grpcurl has no way to produce. Use a
generated gRPC client (any language) and sign each call, as below.The signing contract
Each authenticated unary call carries three metadata entries:
The canonical signing string is four newline-separated lines — note the fixed
POST, the full leading-slash method path, and the SHA-256 of the exact
serialized request bytes:
Sign and place an order
The excerpts below are the signing mechanics (@grpc/grpc-js); concatenated,
they are a minimal signed create/cancel once you supply a session key and a
trading-pair id.
They load a descriptor set rather than .proto files at runtime. Build one from
the public BSR module — no checkout of the Monaco repository is required:
1
Open a session
Generate a session keypair and register its public key: pass
session_public_key to Challenge, sign the returned message with your
wallet, then call Verify. No token is returned — the session is the
registered public key, and Verify returns an expires_at
(see Session expiry and refresh). The same
handshake over REST — POST /api/v1/auth/challenge then
POST /api/v1/auth/verify, with the request and response fields spelled
out — is in the API reference, and
Wallets & Auth covers wallet setup.2
Sign every authenticated call
Serialize the request with the method’s own serializer (the exact prost
bytes on the wire), hash them, build the canonical string, and sign it.
3
Load the client, then create and cancel an order
@grpc/proto-loader cannot parse the source protos (gnostic aggregate
options), so load a prebuilt FileDescriptorSet — make descriptors-gen
from a repo checkout, or buf build buf.build/monaco-sdk/api -o monaco-descriptors.binpb against the public schema registry.Session expiry and refresh
Verify and Refresh both return expires_at, a Unix timestamp (seconds)
for when the session lapses. Track it and refresh proactively, before it
passes: call AuthService.Refresh with an empty RefreshRequest, signed exactly
like any other call but over the method path
/monaco.api.auth.AuthService/Refresh. It returns a fresh expires_at.
Refresh itself
returns UNAUTHENTICATED; at that point you must re-run challenge/verify to
register a new session key. Do not loop-retry the same signed request against
a persistent UNAUTHENTICATED — it will never succeed. A stale or far-future
x-monaco-timestamp is likewise rejected, so always sign with the real current
time in milliseconds, not a cached value.
Error handling
CreateOrderRequest.idempotency_key makes a create safe to replay with the same
normalized payload for 24 hours after acceptance. Generate and persist the key
before signing the first request, then reuse that exact key and payload after an
ambiguous outcome. A different payload returns ALREADY_EXISTS; after 24 hours,
the same key may place a new order. Without the retained key — or after it expires —
never blindly retry a CreateOrder, or you risk a duplicate order. Reconcile on the order WebSocket stream
rather than ListOrders: that read path is served asynchronously from a replica
and can omit an order that did land, and an order rejected before acceptance is never written to it at all (one rejected after acceptance is persisted with status REJECTED). The stream is not replayable — it carries no sequence number to backfill from — and
its fan-out is non-persistent Core NATS, so the service can re-establish an ended upstream
subscription without closing your socket. Subscribe before you send, but treat only a
matching event as an answer: silence is never evidence the order did not land, and no
condition you can observe makes it so. Absent positive evidence, hold rather than
retry. Setting a
clientOrderId makes the stream events self-identifying, but note it is claimed
only while an order rests — see Client Order IDs.
ReplaceOrder behaves differently: it names the original order id, and the
sequencer revalidates that original at sequence time, so once one replacement
succeeds a retry is rejected with NOT_FOUND rather than placing a second order.
That code is ambiguous on its own — the original may instead have filled or been
cancelled — so reconcile to find the replacement’s new order id rather than to
rule out a duplicate.
Golden signing vectors
Deterministic vectors — request, serialized bytes, timestamp, canonical string, and signature for a spot and a marginCreateOrder. They are the source of
truth for the signing contract and are exercised by both the TypeScript example
tests and a server-side Rust test; the copy below is drift-gated against the one
those tests load, so it cannot fall out of date.
No private key is shipped, so an implementation in any language can reproduce
the byte-identical request bytes and canonical signing string and verify each
published signature against signerPublicKey — the check the gateway itself
performs. Producing your own signatures needs your own session key; these
vectors prove the bytes and verification, not re-signing. Pin them in your
signer’s unit tests: serialize each request with a descriptor set built from
buf.build/monaco-sdk/api, and assert you reproduce serializedHex,
bodySha256Hex, and signingString before checking signature against
signerPublicKey.
Golden signing vectors
signingString values above render \n as literal backslash-n inside JSON;
the real canonical string joins its four parts with newline characters.
Signatures are ed25519 over the UTF-8 bytes of that string, hex-encoded.
