Upsert user preferences
Upserts one or more preference keys for the address bound to X-API-Key.
X-API-Key. The body is a partial bag — keys present are upserted, keys absent are untouched. Per-key writes so concurrent writers cannot lose each other’s updates across keys.
The writable address is taken from the API key. No address field is accepted in the body or query string.
Validation runs on every key before any write. If any key fails, the response is 400 with the first failing key and (for enum-typed keys) the allowed set; no row is touched. On success, all writes are issued as a single logged Scylla BATCH so partial application is not observable from a subsequent GET.Authorizations
Hex-encoded Ed25519 public key (64 chars). The public key IS the API key — register it via POST /createApiKey. Required on every authenticated request, both read-only and signed.
Unix time in nanoseconds as a decimal string (e.g. "1713825891591000000"). Millisecond or second epochs are rejected with 401 Unauthorized. Must be within ±30,000 ms (MaxTimestampDriftMs, the drift window stays configured in milliseconds) of server wall-clock, or the request is rejected with 401 Unauthorized. Required on all mutating / credential-creating endpoints. This same value must appear as the ct field in the ordersign typed canonical payload (single-order endpoints) or in each element's ct field (batch endpoints).
Lowercase hex-encoded Ed25519 signature (128 chars).
Single-order endpoints (placeOrder, cancelOrder, modifyOrder, and other non-batch mutating routes) sign over the ordersign typed canonical payload — a compact, key-sorted JSON object built from parsed request fields using engine-native integer values:
placeOrder: {"ad":"0x…","ai":N,[,"c":"…"],"ct":N,"g":N,"m":N,"op":1,"p":N,"q":N,"r":0|1,"s":N,"t":N,"v":1}
cancelOrder: {"ad":"0x…","ai":N,[,"c":"…"],"ct":N,[,"id":"…"],"m":N,"op":2,"v":1}
modifyOrder: {"ad":"0x…","ai":N,[,"c":"…"],"ct":N,[,"id":"…"],"m":N,"op":3,"p":N,"q":N,"v":1}
ct must equal the X-Timestamp header value. Keys in brackets are conditional (omitted when empty). op values: 1=place, 2=cancel, 3=modify. See the ordersign package for field definitions and reference signing code.
Other signed routes (e.g. createApiKey, tokens, userPreferences) still use the legacy scheme: signing_message = X-Timestamp + ACTION + canonicalJSON(body), where ACTION is the camelCase final path segment.
Batch endpoints (batchPlaceOrders, batchCancelOrders) do NOT use this header. They authenticate with per-element typed ordersign signatures embedded in the request body (see the global auth description and the per-field signature descriptions on OrderRequest / CancelOrderRequest).
Read endpoints are authenticated by ?address= (and optionally X-API-Key) only — no signature is required. canonicalJSON(body) is the JSON body with object keys sorted lexicographically at every level and no whitespace; the server canonicalizes the received body before verifying, so only the bytes signed over must be canonical. Required on all mutating / credential-creating endpoints.
Body
Partial preference bag. Each key is upserted; keys absent from the body are untouched. The body must be a non-empty object with at most 64 keys; the total payload must not exceed 128 KiB. Values follow the per-key rules in UserPreferenceValue.
address is NOT accepted — the writable address is the master wallet bound to X-API-Key.
Top-level preference value. Exactly one of string, integer, boolean, or an array (integer[] or string[]). Objects are NOT permitted at the top level — clients that need an object stringify it into a string value (e.g. perpsLayout). lastSignedTOS is stored as a Unix timestamp in seconds.
Response
Preferences accepted and durably written to ScyllaDB. Body echoes the address and the list of keys upserted; clients re-read via GET to observe the new state.
20-byte EVM address as hex: optional 0x or 0X prefix and exactly 40 hexadecimal digits. API responses normalize to lowercase a–f after 0x.
^(0x|0X)?[0-9a-fA-F]{40}$Keys upserted by this PATCH.
Known preference key. The set is closed; PATCHing or DELETEing a key not in this enum returns 400 with allowed listing the accepted set.
favoritedMarkets, perpsLayout, spotLayout, language, numberRepresentation, disabledAlerts, colorTheme, colorRedGreen, lastSignedTOS, isSpotLayoutLocked, isPerpLayoutLocked, isSpotLayoutFlex, isPerpLayoutFlex