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

# gRPC API

> Endpoint, protobuf module, and available services

Use gRPC when you want generated clients, strongly typed request/response contracts, or backend-to-backend integration. Monaco publishes the protobuf module to BSR at [`buf.build/monaco-sdk/api`](https://buf.build/monaco-sdk/api).

## Endpoint

```text theme={null}
https://staging.apimonaco.xyz
```

## Protobuf Module

```text theme={null}
buf.build/monaco-sdk/api:main
```

<Note>
  See the [gRPC protobuf reference](/grpc/protos) for the curated protobuf file, package, service, and message reference.
</Note>

## Services

<Accordion title="AuthService">
  Wallet authentication, session refresh, and session revocation.

  ```protobuf theme={null}
  rpc Challenge(ChallengeRequest) returns (ChallengeResponse);
  rpc Verify(VerifyRequest) returns (VerifyResponse);
  rpc Refresh(RefreshRequest) returns (RefreshResponse);
  rpc Revoke(RevokeRequest) returns (RevokeResponse);
  ```
</Accordion>

<Accordion title="OrdersService">
  Order entry, cancellation, replacement, batch operations, order lookup, and conditional TP/SL order management.

  ```protobuf theme={null}
  rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
  rpc CancelOrder(CancelOrderRequest) returns (CancelOrderResponse);
  rpc BatchCancelOrders(BatchCancelOrdersRequest) returns (BatchCancelOrdersResponse);
  rpc BatchCancelAll(BatchCancelAllRequest) returns (BatchCancelAllResponse);
  rpc BatchCancelAllByPair(BatchCancelAllByPairRequest) returns (BatchCancelAllResponse);
  rpc BatchCreateOrders(BatchCreateOrdersRequest) returns (BatchCreateOrdersResponse);
  rpc BatchReplaceOrders(BatchReplaceOrdersRequest) returns (BatchReplaceOrdersResponse);
  rpc ReplaceOrder(ReplaceOrderRequest) returns (ReplaceOrderResponse);
  rpc GetOrder(GetOrderRequest) returns (GetOrderResponse);
  rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse);
  rpc CancelConditionalOrder(CancelConditionalOrderRequest) returns (CancelConditionalOrderResponse);
  rpc ListConditionalOrders(ListConditionalOrdersRequest) returns (ListConditionalOrdersResponse);
  ```
</Accordion>

<Accordion title="AccountsService">
  Profile, balances, ledger movements, funding payments, sub-accounts, user trades, and portfolio analytics.

  ```protobuf theme={null}
  rpc GetProfile(GetProfileRequest) returns (GetProfileResponse);
  rpc GetBalances(GetBalancesRequest) returns (GetBalancesResponse);
  rpc GetBalanceByAsset(GetBalanceByAssetRequest) returns (GetBalanceByAssetResponse);
  rpc GetMovements(GetMovementsRequest) returns (GetMovementsResponse);
  rpc ListFundingPayments(ListFundingPaymentsRequest) returns (ListFundingPaymentsResponse);
  rpc ListSubAccounts(ListSubAccountsRequest) returns (ListSubAccountsResponse);
  rpc CreateLimit(CreateLimitRequest) returns (CreateLimitResponse);
  rpc UpdateLimit(UpdateLimitRequest) returns (UpdateLimitResponse);
  rpc GetLimits(GetLimitsRequest) returns (GetLimitsResponse);
  rpc GetUserTrades(GetUserTradesRequest) returns (GetUserTradesResponse);
  rpc GetPortfolioStats(GetPortfolioStatsRequest) returns (GetPortfolioStatsResponse);
  rpc GetPortfolioChart(GetPortfolioChartRequest) returns (GetPortfolioChartResponse);
  rpc DeleteLimit(DeleteLimitRequest) returns (DeleteLimitResponse);
  ```
</Accordion>

<Accordion title="MarketService">
  Public market data, candles, screener data, perp market config, funding, open interest, mark price, and index price.

  ```protobuf theme={null}
  rpc ListTradingPairs(ListTradingPairsRequest) returns (ListTradingPairsResponse);
  rpc GetTradingPair(GetTradingPairRequest) returns (GetTradingPairResponse);
  rpc GetCandles(GetCandlesRequest) returns (GetCandlesResponse);
  rpc GetMarketMetadata(GetMarketMetadataRequest) returns (GetMarketMetadataResponse);
  rpc GetScreener(GetScreenerRequest) returns (GetScreenerResponse);
  rpc GetOpenInterest(GetOpenInterestRequest) returns (GetOpenInterestResponse);
  rpc GetFundingState(GetFundingStateRequest) returns (GetFundingStateResponse);
  rpc ListFundingHistory(ListFundingHistoryRequest) returns (ListFundingHistoryResponse);
  rpc GetPerpMarketConfig(GetPerpMarketConfigRequest) returns (GetPerpMarketConfigResponse);
  rpc GetPerpMarketSummary(GetPerpMarketSummaryRequest) returns (GetPerpMarketSummaryResponse);
  rpc GetMarkPrice(GetMarkPriceRequest) returns (GetMarkPriceResponse);
  rpc GetIndexPrice(GetIndexPriceRequest) returns (GetIndexPriceResponse);
  ```
</Accordion>

<Accordion title="OrderbookService">
  Public orderbook snapshots. `GetOrderbookRPC` is a gRPC-only wrapper over the same response type.

  ```protobuf theme={null}
  rpc GetOrderbook(GetOrderbookRequest) returns (GetOrderbookResponse);
  rpc GetOrderbookRPC(GetOrderbookRPCRequest) returns (GetOrderbookResponse);
  ```
</Accordion>

<Accordion title="TradesService">
  Public trade history, single-trade lookup, and authenticated user trade history.

  ```protobuf theme={null}
  rpc GetTrades(GetTradesRequest) returns (GetTradesResponse);
  rpc GetTradesRPC(GetTradesRPCRequest) returns (GetTradesResponse);
  rpc GetTradeById(GetTradeByIdRequest) returns (GetTradeByIdResponse);
  rpc ListUserTrades(ListUserTradesRequest) returns (ListUserTradesResponse);
  ```
</Accordion>

<Accordion title="MarginAccountsService">
  Isolated-margin account summaries, collateral availability, collateral transfers, movements, and order-risk simulation.

  ```protobuf theme={null}
  rpc GetMarginAccountSummary(GetMarginAccountSummaryRequest) returns (GetMarginAccountSummaryResponse);
  rpc GetParentMarginAccountSummary(GetParentMarginAccountSummaryRequest) returns (GetMarginAccountSummaryResponse);
  rpc ListMarginAccounts(ListMarginAccountsRequest) returns (ListMarginAccountsResponse);
  rpc GetAvailableCollateral(GetAvailableCollateralRequest) returns (GetAvailableCollateralResponse);
  rpc TransferCollateralToMarginAccount(TransferCollateralToMarginAccountRequest) returns (TransferCollateralToMarginAccountResponse);
  rpc TransferCollateralFromMarginAccount(TransferCollateralFromMarginAccountRequest) returns (TransferCollateralFromMarginAccountResponse);
  rpc TransferCollateralToParentMarginAccount(TransferCollateralToParentMarginAccountRequest) returns (TransferCollateralToMarginAccountResponse);
  rpc TransferCollateralFromParentMarginAccount(TransferCollateralFromParentMarginAccountRequest) returns (TransferCollateralFromMarginAccountResponse);
  rpc GetMarginAccountMovements(GetMarginAccountMovementsRequest) returns (GetMarginAccountMovementsResponse);
  rpc GetParentMarginAccountMovements(GetParentMarginAccountMovementsRequest) returns (GetMarginAccountMovementsResponse);
  rpc SimulateOrderRisk(SimulateOrderRiskRequest) returns (SimulateOrderRiskResponse);
  rpc SimulateParentMarginOrderRisk(SimulateParentMarginOrderRiskRequest) returns (SimulateOrderRiskResponse);
  rpc TransferCollateralToRiskBucket(TransferCollateralToRiskBucketRequest) returns (TransferCollateralToMarginAccountResponse);
  rpc SimulateRiskBucketOrderRisk(SimulateRiskBucketOrderRiskRequest) returns (SimulateOrderRiskResponse);
  rpc CreateSubAccount(CreateSubAccountRequest) returns (CreateSubAccountResponse);
  rpc CloseSubAccount(CloseSubAccountRequest) returns (CloseSubAccountResponse);
  rpc GetMarginAccountPnl(GetMarginAccountPnlRequest) returns (GetMarginAccountPnlResponse);
  rpc TransferCollateral(TransferCollateralRequest) returns (TransferCollateralResponse);
  rpc TransferCollateralToSubAccount(TransferCollateralToSubAccountRequest) returns (TransferCollateralResponse);
  rpc TransferCollateralFromSubAccount(TransferCollateralFromSubAccountRequest) returns (TransferCollateralResponse);
  ```
</Accordion>

<Accordion title="PositionsService">
  Margin positions, close flows, risk checks, margin adjustments, position history, and TP/SL attachment.

  ```protobuf theme={null}
  rpc ListPositions(ListPositionsRequest) returns (ListPositionsResponse);
  rpc GetPosition(GetPositionRequest) returns (GetPositionResponse);
  rpc ClosePosition(ClosePositionRequest) returns (ClosePositionResponse);
  rpc GetPositionRisk(GetPositionRiskRequest) returns (GetPositionRiskResponse);
  rpc ListPositionHistory(ListPositionHistoryRequest) returns (ListPositionHistoryResponse);
  rpc AttachPositionTpSl(AttachPositionTpSlRequest) returns (AttachPositionTpSlResponse);
  ```
</Accordion>

<Accordion title="ApplicationsService">
  Application configuration (session-authenticated) and public, `client_id`-selected users, balances, orders, movements, and stats.

  ```protobuf theme={null}
  rpc GetConfig(GetConfigRequest) returns (GetConfigResponse);
  rpc ListAppOrders(ListAppOrdersRequest) returns (ListAppOrdersResponse);
  rpc ListAppUsers(ListAppUsersRequest) returns (ListAppUsersResponse);
  rpc ListAppMovements(ListAppMovementsRequest) returns (ListAppMovementsResponse);
  rpc GetAppStats(GetAppStatsRequest) returns (GetAppStatsResponse);
  rpc ListAppBalances(ListAppBalancesRequest) returns (ListAppBalancesResponse);
  ```
</Accordion>

<Accordion title="DelegatedAgentsService">
  Delegated-agent registration, owner lookup, revocation, and delegated-session creation.

  ```protobuf theme={null}
  rpc UpsertDelegatedAgent(UpsertDelegatedAgentRequest) returns (DelegatedAgent);
  rpc ListDelegatedAgents(ListDelegatedAgentsRequest) returns (ListDelegatedAgentsResponse);
  rpc ListDelegatedAgentOwners(ListDelegatedAgentOwnersRequest) returns (ListDelegatedAgentOwnersResponse);
  rpc RevokeDelegatedAgent(RevokeDelegatedAgentRequest) returns (RevokeDelegatedAgentResponse);
  rpc CreateDelegatedSession(CreateDelegatedSessionRequest) returns (CreateDelegatedSessionResponse);
  ```
</Accordion>

<Accordion title="WithdrawalsService">
  Withdrawal initiation, pending-withdrawal listing, and signed calldata lookup.

  ```protobuf theme={null}
  rpc InitiateWithdrawal(InitiateWithdrawalRequest) returns (Withdrawal);
  rpc GetWithdrawal(GetWithdrawalRequest) returns (Withdrawal);
  rpc ListPendingWithdrawals(ListPendingWithdrawalsRequest) returns (ListPendingWithdrawalsResponse);
  ```
</Accordion>

<Accordion title="FeesService">
  Fee simulation for prospective orders.

  ```protobuf theme={null}
  rpc SimulateFees(SimulateFeesRequest) returns (SimulateFeesResponse);
  ```
</Accordion>

<Accordion title="FaucetService">
  Testnet token minting for authenticated users.

  ```protobuf theme={null}
  rpc MintTokens(MintTokensRequest) returns (MintTokensResponse);
  rpc GetFaucetInfo(GetFaucetInfoRequest) returns (GetFaucetInfoResponse);
  ```
</Accordion>

<Accordion title="WhitelistService">
  Public whitelist applications are closed. `SubmitWhitelist` returns `PERMISSION_DENIED` without changing applicant data.

  ```protobuf theme={null}
  rpc SubmitWhitelist(SubmitWhitelistRequest) returns (SubmitWhitelistResponse);
  ```
</Accordion>

<Accordion title="HealthService">
  Public service health check.

  ```protobuf theme={null}
  rpc Check(PublicHealthCheckRequest) returns (PublicHealthCheckResponse);
  ```
</Accordion>

## Generate a Client

<Tabs>
  <Tab title="TypeScript">
    <a id="typescript" />

    ```bash theme={null}
    npm install @bufbuild/protobuf @connectrpc/connect @connectrpc/connect-node
    brew install bufbuild/buf/buf
    ```

    ```yaml theme={null}
    # buf.gen.yaml
    version: v2
    plugins:
      - remote: buf.build/bufbuild/es
        out: gen/ts
      - remote: buf.build/connectrpc/es
        out: gen/ts
    inputs:
      - module: buf.build/monaco-sdk/api:main
    ```

    ```bash theme={null}
    buf generate
    ```
  </Tab>

  <Tab title="Python">
    <a id="python" />

    ```bash theme={null}
    pip install grpcio grpcio-tools
    brew install bufbuild/buf/buf
    ```

    ```yaml theme={null}
    # buf.gen.yaml
    version: v2
    plugins:
      - remote: buf.build/protocolbuffers/python
        out: gen/python
      - remote: buf.build/grpc/python
        out: gen/python
    inputs:
      - module: buf.build/monaco-sdk/api:main
    ```

    ```bash theme={null}
    buf generate
    ```
  </Tab>
</Tabs>

## 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.

<Note>
  `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.
</Note>

### The signing contract

Each authenticated unary call carries three metadata entries:

| Metadata | Value |
| - | - |
| `x-monaco-publickey` | session public key, lowercase hex |
| `x-monaco-timestamp` | request time in **milliseconds** since epoch, as a decimal string (e.g. `1722945600123`; a seconds value like `1722945600` is rejected as out-of-skew) |
| `x-monaco-signature` | ed25519 signature (lowercase hex) of the canonical string below |

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:

```text theme={null}
POST
/<package>.<Service>/<Method>
<timestamp-ms>
<sha256-hex of the prost-encoded request body>
```

Binding the method path matters: empty-body RPCs all encode to the same bytes,
so without the path a signature captured from a benign call could be replayed
against a destructive one.

### 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:

```bash theme={null}
buf build buf.build/monaco-sdk/api -o monaco-descriptors.binpb
```

Rebuild it whenever you pick up a newer version of the module. To verify your
signer against fixed inputs before you send anything, use the
[golden signing vectors](#golden-signing-vectors) below.

<Steps>
  <Step title="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](#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](/api-reference), and
    [Wallets & Auth](/developers/accounts/wallets) covers wallet setup.
  </Step>

  <Step title="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.

    ```ts theme={null}
    import * as grpc from "@grpc/grpc-js";
    import * as protoLoader from "@grpc/proto-loader";
    import { readFileSync } from "fs";
    import { ed25519 } from "@noble/curves/ed25519";
    import { sha256 } from "@noble/hashes/sha2";
    import { bytesToHex, hexToBytes, utf8ToBytes } from "@noble/hashes/utils";

    // ed25519 session private key registered during challenge/verify, as hex.
    const rawKey = process.env.MONACO_SESSION_PRIVATE_KEY ?? "";
    if (!/^[0-9a-fA-F]{64}$/.test(rawKey)) throw new Error("MONACO_SESSION_PRIVATE_KEY must be 32-byte hex");
    const sessionPrivateKey = hexToBytes(rawKey);
    const sessionPublicKey = bytesToHex(ed25519.getPublicKey(sessionPrivateKey));

    function signedMetadata(method: { path: string; requestSerialize: (v: never) => Buffer | Uint8Array }, request: unknown): grpc.Metadata {
      const serialized = method.requestSerialize(request as never);
      const bytes = serialized instanceof Uint8Array ? serialized : new Uint8Array(serialized);
      const timestamp = String(Date.now());
      const hash = bytesToHex(sha256(bytes));
      const signingString = ["POST", method.path, timestamp, hash].join("\n");
      const signature = bytesToHex(ed25519.sign(utf8ToBytes(signingString), sessionPrivateKey));
      const metadata = new grpc.Metadata();
      metadata.set("x-monaco-publickey", sessionPublicKey);
      metadata.set("x-monaco-timestamp", timestamp);
      metadata.set("x-monaco-signature", signature);
      return metadata;
    }
    ```
  </Step>

  <Step title="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.

    ```ts theme={null}
    const packageDefinition = protoLoader.loadFileDescriptorSetFromBuffer(
      readFileSync(new URL("./monaco-descriptors.binpb", import.meta.url)),
      { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true },
    );
    const proto = grpc.loadPackageDefinition(packageDefinition) as any;
    const ordersDef = packageDefinition["monaco.api.orders.OrdersService"] as any;
    const orders = new proto.monaco.api.orders.OrdersService("staging.apimonaco.xyz:443", grpc.credentials.createSsl());

    const pairId = "TRADING_PAIR_UUID"; // from MarketService.ListTradingPairs
    const createOrder = { trading_pair_id: pairId, order_type: "LIMIT", side: "BUY", quantity: "0.01", price: "60000", trading_mode: "SPOT", time_in_force: "GTC" };
    orders.CreateOrder(createOrder, signedMetadata(ordersDef.CreateOrder, createOrder), (err: grpc.ServiceError | null, res: any) => {
      if (err) return console.error(err.code, err.details);
      const cancel = { order_id: res.order_id };
      orders.CancelOrder(cancel, signedMetadata(ordersDef.CancelOrder, cancel), (e: grpc.ServiceError | null, r: any) => console.log(e ?? r));
    });
    ```

    Each call re-signs: the timestamp and body hash differ per request, so
    signatures are never reused.
  </Step>
</Steps>

### 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`.

```ts theme={null}
const authDef = packageDefinition["monaco.api.auth.AuthService"] as any;
const auth = new proto.monaco.api.auth.AuthService("staging.apimonaco.xyz:443", grpc.credentials.createSsl());

const refreshReq = {}; // RefreshRequest has no fields; the signature still binds the method path
auth.Refresh(refreshReq, signedMetadata(authDef.Refresh, refreshReq), (err: grpc.ServiceError | null, res: any) => {
  if (err) return console.error(err.code, err.details); // already expired → re-authenticate
  // res.expires_at (seconds): schedule the next refresh before this time.
});
```

Once a session has **already** expired (or been revoked), `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

| Status | Meaning | Action |
| - | - | - |
| `UNAUTHENTICATED` | missing/invalid signature, expired session, or timestamp skew | re-auth (new session), verify the timestamp is current ms, resign |
| `UNAVAILABLE` with `Authentication temporarily unavailable; retry` | the server could not complete the session check (a database or other dependency is down) — the session itself was not rejected | keep the session and retry with bounded exponential backoff and jitter, capping the attempts; do not re-run challenge/verify |
| `INVALID_ARGUMENT` | malformed request | fix the request; do not retry unchanged |
| `RESOURCE_EXHAUSTED` | rate limited — an order, movement, settings-write, copy trading, registration, read, or public application read budget is spent | read the wait from the `google.rpc.RetryInfo` detail and, on order RPCs, your live budget from the lowercase `x-ratelimit-*` response metadata (reads carry none), back off for that interval, then retry; see [Rate Limits](/developers/rate-limits) |
| `UNAVAILABLE` / `UNKNOWN` with an HTTP `429` in the message | the edge blocked the request — the JSON body's `details.reason` names the rule (`edge_429_flood`, `edge_request_flood`) and `details.retryAfter` the block length | a throttling signal, not a transient fault: back off exponentially with jitter for at least that interval and never retry in a tight loop; see [Rate Limits](/developers/rate-limits#when-you-are-over-budget) |

`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](/developers/trade#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 margin `CreateOrder`. 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`.

```json Golden signing vectors expandable theme={null}
{
  "methodPath": "/monaco.api.orders.OrdersService/CreateOrder",
  "timestampMs": 1700000000000,
  "signerPublicKey": "fff86b85a717d5b1564aa6047550094eb150b5fc4ee1ec9de783923c0cb32ef3",
  "vectors": {
    "spot": {
      "request": {
        "trading_pair_id": "afae0e16-2d05-4ee9-9ee8-afae0e162d05",
        "order_type": "LIMIT",
        "side": "BUY",
        "quantity": "0.01",
        "price": "60000",
        "trading_mode": "SPOT",
        "time_in_force": "GTC"
      },
      "serializedHex": "0a2461666165306531362d326430352d346565392d396565382d61666165306531363264303512054c494d49541a03425559220536303030302a04302e3031320453504f545203475443",
      "bodySha256Hex": "22ba7932ddf7d17a79d341f46557a2bfa02297710356d8da06d83e234a56d6b8",
      "signingString": "POST\n/monaco.api.orders.OrdersService/CreateOrder\n1700000000000\n22ba7932ddf7d17a79d341f46557a2bfa02297710356d8da06d83e234a56d6b8",
      "signature": "6f27e6bd39fe3b4c942a762111e9b23217252e0260f1b0148c182f9c72ef43f7c0b8c340089f01e04c614c5b4186ddd43ed4e5e83f8d0ea6ff0c9eab309e6e03"
    },
    "margin": {
      "request": {
        "trading_pair_id": "123e4567-e89b-12d3-a456-426614174000",
        "order_type": "LIMIT",
        "side": "BUY",
        "quantity": "0.01",
        "price": "60000",
        "trading_mode": "MARGIN",
        "margin_account_id": "123e4567-e89b-12d3-a456-426614174000",
        "leverage": "5",
        "time_in_force": "GTC"
      },
      "serializedHex": "0a2431323365343536372d653839622d313264332d613435362d34323636313431373430303012054c494d49541a03425559220536303030302a04302e303132064d415247494e52034754435a2431323365343536372d653839622d313264332d613435362d3432363631343137343030306a0135",
      "bodySha256Hex": "fff189b1eb156eaa41e89a1cecdd1d365d27f6f219f3b5017bcf8e2f31666eb9",
      "signingString": "POST\n/monaco.api.orders.OrdersService/CreateOrder\n1700000000000\nfff189b1eb156eaa41e89a1cecdd1d365d27f6f219f3b5017bcf8e2f31666eb9",
      "signature": "348b35353535f3158f67a1d9ecde090bcd483eee0f92b3fc9b1e3f42f75dfa6c1a9ffcb58ed638d5e6f29db3844e75bd3a2e7e9241214e247b345fb20614b100"
    }
  }
}
```

The `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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.