Skip to main content
Trades are placed through PerpsClient.placeOrder, which builds the action, authorizes it through the registered provider plugin, and submits it in one call. Order actions use the plugin’s SDK-managed credential, so the user’s wallet is not prompted per trade.
The examples on this page use Hyperliquid (provider: 'hyperliquid'). The trading API is provider-agnostic — replace the provider value with any supported venue from getProviders(). The placeOrder call shape is identical for every provider; each provider signs with its own configured signer.

placeOrder

Once the user has provisioned an agent (see Signing Model and Hyperliquid / Setup), placing an order is a single call:
The request enums (OrderSide, OrderType, TimeInForce, MarginMode, ActionType) are TypeScript enums exported from @lifi/perps-sdk; string literals such as 'BUY' do not typecheck. Internally this is the create -> authorize -> execute pattern from Action Pattern:
The SDK handles all three steps. When leverage is provided, the backend always prepends an updateLeverage step; both actions are signed and submitted together, so result.results[] has one entry per action. Omit leverage to leave the venue’s current setting untouched (getMarketSettings reads it).
A venue refusal does not throw. It arrives inside a successful 202 response as a result with status Failed, with error and an optional errorCode. An unclear venue answer has status Indeterminate: read the account before you retry. Submitted and Accepted mean that the venue acknowledged the order, not that it is filled. Track its outcome with Order Status. See Execution reports and Error Handling.
Every provider action fails with 401 / 2012 (TermsNotAccepted) until the address has accepted the current terms version. See Error Handling.
placeOrder supports MARKET and LIMIT. Place stop and take-profit orders through the takeProfit / stopLoss fields or placeTriggerOrder. After a write, the venue’s account, settings, orders and fills reads can return pre-write data for a couple of seconds. Update your UI optimistically and re-read once after a short delay.

Order Types

Market Order

Executes immediately at the best available price. The price field acts as a slippage limit.

Limit Order

Rests on the orderbook until filled or cancelled.

Formatting prices and sizes

Prices and sizes must sit on the venue’s tick and lot grid, or the order is rejected. Snap them through the market’s own provider. Sizes are truncated, never rounded up.

Time in Force options

GTC is accepted on every provider. Hyperliquid
  • GTC is the default and rests until filled or cancelled.
  • IOC fills immediately and cancels any unfilled portion.
  • POST_ONLY is maker-only and is cancelled if it would match immediately.
  • GTT needs expiresAt. The backend returns a validation error for a GTT order without it.
Lighter
  • GTC is the default and rests until filled or cancelled.
  • IOC fills immediately and cancels any unfilled portion.
  • POST_ONLY is maker-only and is cancelled if it would match immediately.
  • Any other value falls back to GTC.
Ondo
  • GTC is the default for limit orders.
  • IOC fills immediately and cancels any unfilled portion.
  • POST_ONLY is submitted as GTC with the post-only flag.
  • GTT is rejected.
  • Market orders. On Lighter and Ondo, a market order accepts only IOC (or no timeInForce). On Hyperliquid, a market order without timeInForce is sent as a frontend-market order.
  • Ondo reduce-only limits. Reduce-only limit orders are coerced to IOC.
  • expiresAt. Only Lighter honours it, and only on GTC and POST_ONLY orders; IOC and market-style orders with expiresAt throw. Hyperliquid ignores it. Ondo rejects it.
Lighter has no true good-til-cancelled. A resting order placed on Lighter is reported back (e.g. from getOrders) with timeInForce: 'GTT', even if you submitted it as GTC. Don’t rely on reading back the exact TIF you sent on Lighter.
API Reference: POST /createAction · /executeAction