Skip to main content

Overview

This guide explains how to submit buy or sell orders for assets in a user’s portfolio using the Investment Orders endpoint POST /investments/orders.
All order operations require M2M authentication with a bearer token and the x-user-id header to specify which user’s orders to manage.

Key differences between order types

  • Buy orders: Specify amount (monetary value to invest). You must verify the user has sufficient cash balance before submitting.
  • Sell orders: Specify quantity (asset units to sell). You must verify the user has sufficient holdings before submitting.

Prerequisites

  • Access to API endpoints
  • M2M bearer token with required scopes
  • x-user-id header (user identifier, MongoDB ObjectId format)

Buy order flow

Use this flow to purchase an asset for a user’s portfolio.
1

Verify available cash

Before submitting a buy order, retrieve the user’s cash balance to ensure sufficient funds are available.Call GET /cash to retrieve the available cash balance across all currencies.
const BASE = 'https://api.wealthyhood.com';

async function getCashBalance({ token, userId }) {
  const res = await fetch(`${BASE}/cash`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'x-user-id': userId,
      'Accept': 'application/json'
    }
  });
  if (!res.ok) throw new Error(`Failed to get cash: ${res.status}`);
  return res.json();
}
Compare the available cash balance in the target currency with the intended purchase amount before proceeding.
2

Get asset buy preview

Get a preview of the asset buy transaction to show execution options, fees breakdown, and estimated quantity.Call POST /investments/asset/preview with the asset ISIN, amount, and side set to “buy”.
const BASE = 'https://api.wealthyhood.com';

async function getAssetBuyPreview({ token, userId, isin, amount }) {
  const res = await fetch(`${BASE}/investments/asset/preview`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'x-user-id': userId,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      isin,
      amount,
      side: 'buy'
    })
  });
  if (!res.ok) throw new Error(`Failed to get preview: ${res.status}`);
  return res.json();
}
Display both express and smart execution options:
  • Express (real-time): Faster execution but may incur additional fees
  • Smart (market hours): Lower fees but executes during market hours
Show the fees breakdown (fees.express vs fees.smart) and execution windows to help users make informed decisions.
3

Submit the buy order

Call POST /investments/orders with optional query params:
  • paymentMethod: cash (default) or gift
  • executeEtfOrdersInRealtime: true only for users enabled for real-time ETF execution
const BASE = 'https://api.wealthyhood.com';

async function submitBuyOrder({ token, userId, isin, amount, paymentMethod = 'cash', executeEtfOrdersInRealtime }) {
  const url = new URL(`${BASE}/investments/orders`);
  if (paymentMethod) url.searchParams.set('paymentMethod', paymentMethod);
  if (typeof executeEtfOrdersInRealtime === 'boolean') {
    url.searchParams.set('executeEtfOrdersInRealtime', String(executeEtfOrdersInRealtime));
  }

  const res = await fetch(url.toString(), {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'x-user-id': userId,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      side: 'buy',
      isin,
      amount
    })
  });
  if (!res.ok) throw new Error(`Order failed: ${res.status}`);
  return res.json();
}
Use executeEtfOrdersInRealtime=true only for eligible users and only for ETFs.
4

Render the transaction in UI

The response is an AssetTransaction with display-ready fields such as displayAmount, displayQuantity, executionWindow, fees, and nested orders.
  • Show execution window: Use executionWindow.type (e.g., real-time or market-hours) and expectedExecutionDate when provided.
  • Show fees: Display fees.commission.amount, fees.fx.amount, etc., when present.
  • Show order summary: From orders[0], read isin, side, status, and displayQuantity.
Successful submission returns HTTP 200 with a populated transaction object.

Sell order flow

Use this flow to sell an asset from a user’s portfolio.
1

Verify portfolio holdings

Before submitting a sell order, retrieve the user’s portfolio holdings to ensure sufficient quantity of the asset is available.Call GET /portfolios/{id} to retrieve holdings with asset details and quantities.
const BASE = 'https://api.wealthyhood.com';

async function getPortfolioHoldings({ token, userId, portfolioId }) {
  const res = await fetch(`${BASE}/portfolios/${portfolioId}`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'x-user-id': userId,
      'Accept': 'application/json'
    }
  });
  if (!res.ok) throw new Error(`Failed to get holdings: ${res.status}`);
  return res.json();
}
Verify that the portfolio holdings contain sufficient quantity of the asset (by ISIN) before proceeding with the sell order.
The holdings response includes an array of holdings, each with:
  • asset.isin: ISIN of the asset
  • quantity: Available quantity to sell
  • asset.currentTicker.price: Current price per unit
2

Get asset sell preview

Get a preview of the asset sell transaction to show execution options, fees breakdown, and estimated proceeds.Call POST /investments/asset/preview with the asset ISIN, quantity, and side set to “sell”.
const BASE = 'https://api.wealthyhood.com';

async function getAssetSellPreview({ token, userId, isin, quantity }) {
  const res = await fetch(`${BASE}/investments/asset/preview`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'x-user-id': userId,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      isin,
      quantity,
      side: 'sell'
    })
  });
  if (!res.ok) throw new Error(`Failed to get preview: ${res.status}`);
  return res.json();
}
Display both express and smart execution options:
  • Express (real-time): Faster execution but may incur additional fees
  • Smart (market hours): Lower fees but executes during market hours
Show the estimated proceeds (orders.express and orders.smart with money field) and fees breakdown.
Aggregated sell orders for ETFs are rejected for real-time-enabled users. Check hasETFOrders in the preview response to determine if this applies.
3

Submit the sell order

Call POST /investments/orders with optional query params:
  • paymentMethod: cash (default) or gift
  • executeEtfOrdersInRealtime: true only for users enabled for real-time ETF execution
const BASE = 'https://api.wealthyhood.com';

async function submitSellOrder({ token, userId, isin, quantity, paymentMethod = 'cash', executeEtfOrdersInRealtime }) {
  const url = new URL(`${BASE}/investments/orders`);
  if (paymentMethod) url.searchParams.set('paymentMethod', paymentMethod);
  if (typeof executeEtfOrdersInRealtime === 'boolean') {
    url.searchParams.set('executeEtfOrdersInRealtime', String(executeEtfOrdersInRealtime));
  }

  const res = await fetch(url.toString(), {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'x-user-id': userId,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      side: 'sell',
      isin,
      quantity
    })
  });
  if (!res.ok) throw new Error(`Order failed: ${res.status}`);
  return res.json();
}
Aggregated sell orders for ETFs are rejected for real-time-enabled users. If the preview shows hasETFOrders: true and the user is real-time enabled, you should set executeEtfOrdersInRealtime=false or show an error message.
4

Render the transaction in UI

The response is an AssetTransaction with display-ready fields such as displayAmount, displayQuantity, executionWindow, fees, and nested orders.
  • Show execution window: Use executionWindow.type (e.g., real-time or market-hours) and expectedExecutionDate when provided.
  • Show fees: Display fees.commission.amount, fees.fx.amount, etc., when present.
  • Show order summary: From orders[0], read isin, side, status, and displayQuantity.
Successful submission returns HTTP 200 with a populated transaction object.

Error handling

  • 400 Bad Request: Validation or business rule violation (e.g., invalid amount, missing required field, invalid ISIN).
  • 401/403: Authentication/authorization failures.
  • 404 Not Found: Portfolio or related resource not found.

See also