Skip to main content
POST
Sell spot collateral to settle the account's loan
The response body shown here is a static example, not live data. After you click Send, your live result appears in a separate panel headed 200 OK. The panel under the status-code tabs is a fixed sample from the spec — its field values (fees, prices, sizes, IDs, timestamps) are placeholders. Use Send, or call the endpoint, for current values.
Voluntarily settle an outstanding loan balance by selling spot collateral to the exchange’s liquidity provider at the mark price minus a configured spread (25 bps by default). The sale proceeds land as account cash, which pays down the loan (and any accrued interest) immediately — the way out of a loan that remains after closing perp positions at a loss, without depositing external funds or waiting for liquidation. Requires address query parameter or JSON address field (must match X-API-Key’s master if both are present). accountIndex is required in the signed JSON body (default 0); it may also be sent as ?accountIndex=. If both are present they must match. maxQuantity (asset units) and minPrice (USD per unit) are decimal strings. Both are explicit, signed bounds: the engine sells at most maxQuantity, never below minPrice, and never more than the loan (plus accrued interest) needs at the execution price — so a client settling in full can safely overshoot the quantity and rely on the debt cap, and any price improvement passes through to the account. Partial fills are possible when the buyer can only absorb part; compare executedQuantity in the response.

Response behavior

Asynchronous. The endpoint returns 200 OK (engine executed the settlement — check executedQuantity, repaidAmount, newLoanBalance), 422 Unprocessable Entity (engine rejected — see rejectReason), or 202 Accepted (request forwarded but the confirmation didn’t arrive within the timeout; the engine may still execute — observe the account WebSocket channels to confirm).

Authorizations

X-API-Key
string
header
required

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.

X-Timestamp
string
header
required

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

X-Signature
string
header
required

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:

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, batchModifyOrders) 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 / ModifyOrderRequest).

Read endpoints are authenticated by ?address= (and optionally X-API-Key) only — no signature is required. The one exception is GET /v1/affiliate/inviteCodes, which returns bearer secrets and therefore requires the full header triple; with no body its signing message is X-Timestamp + ACTION. 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.

Query Parameters

address
string
required

Master Ethereum address for this API key (must match address from POST /createApiKey for the same key). Required on REST for account-scoped reads and for place/cancel. Invalid hex → 400; mismatch with key → 403.

20-byte EVM address as hex: optional 0x or 0X prefix and exactly 40 hexadecimal digits. API responses normalize to lowercase af after 0x.

Pattern: ^(0x|0X)?[0-9a-fA-F]{40}$
accountIndex
integer
default:0

Subaccount index (0–9) to scope the request to. Defaults to 0 (the primary account). Values above 9 → 400.

Required range: 0 <= x <= 9

Body

application/json
address
string
required

Master EVM address for this account. May also be supplied via the ?address= query parameter; if both are present they must match. When X-API-Key is enforced the body / query address must match the key's master, otherwise the request is rejected with HTTP 403.

Pattern: ^(0x|0X)?[0-9a-fA-F]{40}$
spotAssetId
integer
required

The spot collateral market to sell (1 = SPY, 2 = TSLA) — the same spotAssetId namespace /v1/spotAssets and spot deposits / withdrawals use, NOT a perp market id. 0 (USDG) is invalid: the quote currency is not sellable collateral.

Required range: x >= 1
maxQuantity
string
required

Maximum asset quantity to sell, as a decimal string in asset units (e.g. "2.5" for 2.5 shares). Must be positive and a multiple of the asset's quantum. This is an explicit signed upper bound — there is no "settle everything" sentinel. The engine executes the minimum of this quantity, what the loan (plus accrued interest) needs at the execution price, the account's holding, and the buyer's capacity — so overshooting the debt is safe (execution is capped, reduce-only style), partial fills are possible (compare executedQuantity in the response), and any price improvement passes through to the account.

Example:

"2.5"

minPrice
string
required

Lowest acceptable per-unit execution price, as a decimal USD string. Must be positive and a multiple of the asset's tick size. The engine executes at the spread-discounted mark (mark × (1 − settleSpread), spread 25 bps by default); if that price falls below minPrice the request is rejected with PRICE_BELOW_MIN_PRICE. This is the limit-price analog: it pins the signed intent against mark moves between sign and execute.

Example:

"498.75"

accountIndex
integer
default:0

Subaccount index (0–9). Required in the signed body; omit or send 0 for the primary account. May also be supplied via the ?accountIndex= query parameter; if both are present they must match. When X-API-Key is enforced the index must match the key's authorized subaccount, otherwise the request is rejected with HTTP 403.

Required range: 0 <= x <= 9

Response

Engine executed the settlement (status: APPLIED) — possibly a partial fill; compare executedQuantity to maxQuantity.

requestId
string
required

Server-generated UUID identifying this request in subsequent WebSocket events.

address
string
required

20-byte EVM address as hex: optional 0x or 0X prefix and exactly 40 hexadecimal digits. API responses normalize to lowercase af after 0x.

Pattern: ^(0x|0X)?[0-9a-fA-F]{40}$
accountIndex
integer
required

Account index (account index, 0–9). Identifies the account for orders, positions, fills, and API keys.

Required range: 0 <= x <= 9
spotAssetId
integer
required

The spot collateral market sold, echoed from the request.

maxQuantity
string
required

The requested maximum quantity, echoed back as a decimal string in asset units.

Example:

"2.5"

minPrice
string
required

The requested price floor, echoed back as a decimal USD string.

Example:

"498.75"

executedQuantity
string
required

Asset quantity actually sold, as a decimal string in asset units. At most maxQuantity, and less when the loan needed less, the holding was smaller, or the buyer could only absorb part (a partial fill). "0" on ACK and REJECTED.

Examples:

"1.5"

"0"

executionPrice
string
required

The spread-discounted per-unit price paid, as a decimal USD string. "0" on ACK and REJECTED.

Examples:

"498.75"

"0"

proceeds
string
required

USDG credited to the account (executedQuantity × executionPrice), in dollars as a decimal string. "0" on ACK and REJECTED.

Examples:

"748.5"

"0"

repaidAmount
string
required

The loan reduction in dollars as a decimal string, including accrued interest settled by the same event. "0" on ACK and REJECTED.

Examples:

"748.5"

"0"

newLoanBalance
string
required

The post-settlement loan balance in dollars as a decimal string. "0" on ACK (engine not yet confirmed); the unchanged balance on REJECTED.

Examples:

"1.5"

"0"

status
enum<string>
required

Engine-lifecycle marker for the settleLoan response body. Distinct from the HTTP status code, which is a transport-level signal.

  • ACK (HTTP 202): the request was accepted; the engine has not yet confirmed. Not a failure — subscribe to the account WebSocket channels for the engine-confirmed settlement.
  • APPLIED (HTTP 200): engine executed the settlement — possibly a partial fill; compare executedQuantity to maxQuantity.
  • REJECTED (HTTP 422): engine rejected the settlement; see rejectReason.
Available options:
ACK,
APPLIED,
REJECTED
rejectReason
enum<string>

Engine-side rejection reason, present on HTTP 422 responses:

  • FEATURE_DISABLED: loan settlement is not enabled on this deployment.
  • UNKNOWN_MARKET: spotAssetId is not an allocated spot collateral market.
  • MISSING_MARK_PRICE: the asset has no live mark price (a transient oracle gap), so the settlement cannot be priced. Retry once pricing resumes.
  • INVALID_QUANTITY: maxQuantity was non-positive or malformed at the engine boundary.
  • INVALID_MIN_PRICE: minPrice was non-positive or malformed at the engine boundary.
  • NO_LOAN: the account has no outstanding loan balance to settle.
  • ACCOUNT_IN_WIND_DOWN: the account is in post-liquidation wind-down; its estate resolves through the liquidation machinery and cannot be voluntarily settled.
  • NO_HOLDING: the account holds none of the named asset.
  • PRICE_BELOW_MIN_PRICE: the spread-discounted execution price fell below the signed minPrice floor. Nothing was sold; re-sign at a lower floor or wait for the mark to recover.
  • NO_BUYER: no liquidity provider is currently able to buy the asset (none configured for the segment, or insufficient capacity). Nothing was sold; retry later.
Available options:
FEATURE_DISABLED,
UNKNOWN_MARKET,
MISSING_MARK_PRICE,
INVALID_QUANTITY,
INVALID_MIN_PRICE,
NO_LOAN,
ACCOUNT_IN_WIND_DOWN,
NO_HOLDING,
PRICE_BELOW_MIN_PRICE,
NO_BUYER