Skip to main content
Change the limit price or trigger price of a resting order — move a stop-loss, take profit sooner, reprice a resting limit — with PATCH /orders/{orderId}, no need to cancel and re-place, and the order keeps its orderId. Like cancelling, updating requires a wallet signature in addition to your API key: you construct a short plaintext message, sign it with the funder wallet, and send both with the new price(s). The update signature is offchain and gasless; no transaction is sent, and the original order signature and token approval stay untouched.

What can be updated

An update carries a new limit price, a new trigger price, or both, depending on the order type: A bracket cannot be updated: the request returns 422. Cancel it and place a new one. Limit price — send exactly one of limitNotionalPrice (USD price of the target asset) or limitCrossPrice (pair rate, target / contra). You are not restricted to the basis the order was placed with; the order carries whichever basis was set last. On the trigger types the limit is the post-trigger execution limit: the price the order fills at once the trigger has fired. See Pricing basis. Trigger price — send a trigger object with the same shape as submit’s: exactly one of notionalPrice or crossPrice, plus triggerType (lower or upper). A trigger update moves the threshold only:
The trigger’s direction and price basis are fixed for the life of the order. triggerType must match the direction the trigger was placed with — always lower for stop-loss, always upper for take-profit, and whichever direction your stop order used (typically upper for a breakout buy). The price must likewise be in the same basis the trigger was placed with — a trigger placed with notionalPrice can only be updated with notionalPrice. This differs from the limit price, whose basis you may switch freely. A new trigger price that the market already satisfies is rejected — an update never causes an immediate fire.
To change any other parameter of an order, cancel it and place a new one.

The update message

Construct this exact UTF-8 string — a three-line header followed by one price line per updated value, joined by single newlines:
In code — a stop-limit update moving both prices:
The message bytes are identical on EVM and Solana; only the signing primitive differs. The server validates the message byte-for-byte, so:
  • The separator in the header is an em dash (—, U+2014), not a hyphen. Unlike the cancel header, there is no v1 — don’t copy the header from your cancel flow.
  • Line breaks are single \n characters. CRLF, blank lines, and a trailing newline are all rejected.
  • <orderId> is the order’s UUID exactly as returned by POST /order.
  • Issued At is an RFC3339 UTC timestamp and must be within 1 minute of server time — construct and sign the message immediately before sending. The stamp is what stops a captured signature from being replayed later to revert a newer price. Sub-second precision (e.g. new Date().toISOString()) is supported and recommended: two updates on the same order in the same second need distinct stamps to produce distinct signed bytes.
  • The Limit line is exactly one of Limit Cross Price: <decimal> or Limit Notional Price: <decimal>, matching the limit field you send in the request body.
  • The Trigger line is Trigger <Lower|Upper> <Cross|Notional> Price: <decimal> — direction matching the trigger.triggerType you send (lower → Lower, upper → Upper), kind matching which trigger price field is set.
  • Include only the lines for values you are updating, each at most once, with at least one present; when both are present the Limit line comes first.
  • Every decimal must be byte-identical to the request value — if the body says "4000", the message must say 4000, not 4000.0. No normalization is applied.
Send the exact string you signed back as updateMessage in the request body. The message is plaintext by design: a wallet’s message-signing prompt shows the user precisely which order is being repriced, and to what.

Signing the message

Sign with the funder wallet — the same wallet that signed the order. A signature from any other key is rejected.
On EVM, the signature is verified onchain against the funder address using ERC-1271 with ERC-6492 support. EOAs, deployed smart accounts, and counterfactual (not-yet-deployed) smart accounts all work — sign with whatever personal_sign flow the wallet provides.

Examples

Move a stop-loss

The headline use case: raise a resting stop-loss’s trigger to $1,800 (notional basis) without touching anything else. Shown for EVM; on Solana only the signing primitive differs, exactly as in the limit examples below.
A take-profit update is identical with triggerType: "upper" and a Trigger Upper ... line. On a stop-limit or take-profit-limit you can move the limit in the same request: add the limit field to the body and its Limit ... Price: line to the message, above the trigger line.

Reprice a resting limit

Both examples move a resting limit order’s price to $4,000 (notional basis). To price in the pair rate instead, use Limit Cross Price: in the message and limitCrossPrice in the body.

EVM

SVM (Solana)

What an update does

An accepted update is executed as a cancel-and-replace under the same orderId: the remaining quantity is re-placed with the new price(s), and the order’s identity is stable everywhere — GET /orders/{orderId}, the Orders WebSocket, and any state you key by orderId. Partial fills are kept. Fills that already settled onchain are final; only the unfilled remainder is re-placed. The update is visible on the WebSocket. While the update is in flight, order frames carry pendingUpdate: true; once applied, the flag clears and the frames carry the new limitNotionalPrice / limitCrossPrice and trigger. If the flag clears and the prices are unchanged, the update was rejected downstream.

Which orders can be updated

An order is updatable while it is live and unfilled under its orderId — status ORDER_STATUS_ACCEPTED or ORDER_STATUS_PARTIALLY_FILLED, whether it is resting at a limit or armed waiting on a trigger. See Order statuses. For the trigger types, what is updatable depends on whether the trigger has fired:
  • Before the trigger fires, the trigger price can move, and on stop-limit / take-profit-limit the post-trigger execution limit can move with it — either or both in one update.
  • After the trigger fires, a stop-limit / take-profit-limit order is an ordinary resting limit order; a limit update reprices it in flight — cancelled and re-placed at the new price under the same orderId. A trigger update at that point is rejected: there is no trigger left to move. (stop and take-profit execute at market on firing, so there is nothing to update after the fire.)
The mechanics below apply to every updatable type:
  • One update at a time. While an update is in flight, another PATCH returns 422 (order already has a pending update). Wait for the WebSocket frame with the new price, or re-read the order, before sending the next one.
  • Terminal orders can’t be updated. Orders in ORDER_STATUS_FILLED, ORDER_STATUS_CANCELLED, ORDER_STATUS_REJECTED, or ORDER_STATUS_TERMINATED return 422.
  • A trigger order that fires on entry has nothing to reprice. If the market already satisfies the trigger the instant the order is placed, it executes without ever resting; an update for it returns 404, the same as an orderId that never existed.
  • An update can race a fill. If the order fills before the update lands, the request returns 422 — treat that as the order having executed at the old price, not as a failure to retry.
  • 404 covers both a missing order and a failed signature. If you get a 404 for an order you know exists, check the signature and the message bytes.