Webhooks
Trade It can notify you via webhook when events like trade fills and account connections happen. Up to 5 endpoints per organization can be configured at Organizations → Webhooks.
Events cover your organization's active members, and each endpoint only receives the event types it subscribes to.

Payload
Every event is a JSON POST with the same envelope:
{
"id": "0a67c0b8-57ae-4cdf-ab8b-5b249e83c100",
"type": "TRADE_UPDATED",
"created_at": "2026-10-07T14:31:12.000Z",
"data": {}
}| Field | Description |
|---|---|
id | Unique event ID, also sent as the TradeIt-Event-Id header. Retries reuse it, so deduplicate on it. |
type | One of the event types below. |
created_at | When the event happened, as an ISO 8601 timestamp. |
data | Snapshot of the Account or Trade the event is about, taken when it happened. Private brokerage identifiers like external_id are left out. |
Event types
ACCOUNT_ADDED
Sent when a brokerage account is linked. Connecting a brokerage sends one for each of its accounts, and a later sync sends one for any new account the brokerage reports.
Data: Account
{
"id": "5f0c7e7a-3b0e-4a43-9a5e-1c2f7d8e9b10",
"type": "ACCOUNT_ADDED",
"created_at": "2026-10-07T14:30:00.000Z",
"data": {
"id": 304,
"connection_id": 395,
"name": "Robinhood x1234",
"balance": { "amount": 15420.5, "currency": "USD" },
"cash": { "amount": 3420.5, "currency": "USD" },
"buying_power": { "amount": 3420.5, "currency": "USD" },
"synced_at": "2026-10-07T14:30:00.000Z",
"holdings_synced_at": null,
"trades_synced_at": null,
"created_at": "2026-10-07T14:30:00.000Z",
"updated_at": "2026-10-07T14:30:00.000Z"
}
}ACCOUNT_DELETED
Sent when an account is removed, either because the user deleted its brokerage connection or because the brokerage removed the account from the connection.
Data: Account as it was just before removal
Note
The account's id stops working in API requests.
{
"id": "c3d1a9e2-7f4b-4c8e-b1a6-2e9f0d7c5b34",
"type": "ACCOUNT_DELETED",
"created_at": "2026-10-09T18:02:44.000Z",
"data": {
"id": 304,
"connection_id": 395,
"name": "Robinhood x1234",
"balance": { "amount": 15873.12, "currency": "USD" },
"cash": { "amount": 2412.08, "currency": "USD" },
"buying_power": { "amount": 2412.08, "currency": "USD" },
"synced_at": "2026-10-09T17:45:10.000Z",
"holdings_synced_at": "2026-10-09T17:45:12.000Z",
"trades_synced_at": "2026-10-09T06:00:03.000Z",
"created_at": "2026-10-07T14:30:00.000Z",
"updated_at": "2026-10-09T17:45:12.000Z"
}
}ACCOUNT_DISCONNECTED
Sent when an account's brokerage connection breaks, usually because the brokerage revoked access or the login expired. Trades and syncs for the account fail until the user reconnects the brokerage in Trade It.
Data: Account
Note
Each account on the connection gets its own event, so a login with three accounts sends three.
{
"id": "8b2e4f61-0d3a-4e7c-9f15-6a8c2b4d1e90",
"type": "ACCOUNT_DISCONNECTED",
"created_at": "2026-10-08T03:12:09.000Z",
"data": {
"id": 304,
"connection_id": 395,
"name": "Robinhood x1234",
"balance": { "amount": 15873.12, "currency": "USD" },
"cash": { "amount": 2412.08, "currency": "USD" },
"buying_power": { "amount": 2412.08, "currency": "USD" },
"synced_at": "2026-10-07T21:00:04.000Z",
"holdings_synced_at": "2026-10-07T21:00:06.000Z",
"trades_synced_at": "2026-10-07T06:00:03.000Z",
"created_at": "2026-10-07T14:30:00.000Z",
"updated_at": "2026-10-07T21:00:06.000Z"
}
}ACCOUNT_RECONNECTED
Sent when the user reconnects a brokerage, whether or not the connection had broken. Each account on the connection gets its own event.
Data: Account
Note
Accounts the brokerage reports for the first time arrive as ACCOUNT_ADDED instead.
{
"id": "e47a0c95-2b6d-4f13-8c7e-91d5a3f6b208",
"type": "ACCOUNT_RECONNECTED",
"created_at": "2026-10-08T15:20:31.000Z",
"data": {
"id": 304,
"connection_id": 395,
"name": "Robinhood x1234",
"balance": { "amount": 15873.12, "currency": "USD" },
"cash": { "amount": 2412.08, "currency": "USD" },
"buying_power": { "amount": 2412.08, "currency": "USD" },
"synced_at": "2026-10-07T21:00:04.000Z",
"holdings_synced_at": "2026-10-07T21:00:06.000Z",
"trades_synced_at": "2026-10-07T06:00:03.000Z",
"created_at": "2026-10-07T14:30:00.000Z",
"updated_at": "2026-10-07T21:00:06.000Z"
}
}TRADE_UPDATED
Sent when a trade reaches a final status: complete, canceled, failed, or disconnected, which means the brokerage connection broke before the order resolved.
Data: Trade without the nested asset; use asset_id to look it up
Note
Partial fills and other in-progress changes are not sent.
{
"id": "0a67c0b8-57ae-4cdf-ab8b-5b249e83c100",
"type": "TRADE_UPDATED",
"created_at": "2026-10-07T14:31:12.000Z",
"data": {
"id": 842,
"user_id": 1,
"account_id": 304,
"asset_id": 88,
"source": "api",
"type": 0,
"action": "buy",
"direction": null,
"order_type": "limit",
"time_in_force": "day",
"status": "complete",
"total_units": 4,
"filled_units": 4,
"canceled_units": 0,
"limit_price": { "amount": 250, "currency": "USD" },
"stop_price": null,
"take_profit": null,
"stop_loss": null,
"notional_amount": null,
"execution_price": { "amount": 248.12, "currency": "USD" },
"fee": null,
"legs": null,
"event": null,
"error_message": null,
"analysis": null,
"image_url": null,
"placed_at": "2026-10-07T14:30:58.000Z",
"executed_at": "2026-10-07T14:31:10.000Z",
"created_at": "2026-10-07T14:30:41.000Z",
"updated_at": "2026-10-07T14:31:12.000Z"
}
}Verify the signature
Requests include TradeIt-Signature: t=<unix-seconds>,v1=<hex-hmac> and TradeIt-Event-Id. Compute HMAC-SHA256 with your endpoint's complete whsec_… secret over ${timestamp}.${rawBody}. Compare signatures using a constant-time comparison and reject timestamps more than five minutes from your server's clock. Verify before parsing or acting on the event.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWebhook(rawBody: Buffer, header: string, secret: string) {
const parts = header.split(',');
const timestampText = parts.find(part => part.startsWith('t='))?.slice(2) ?? '';
const supplied = parts.find(part => part.startsWith('v1='))?.slice(3) ?? '';
const timestamp = Number(timestampText);
if (!/^\d+$/.test(timestampText) || !Number.isSafeInteger(timestamp) || Math.abs(Date.now() / 1000 - timestamp) > 300 || !/^[a-f0-9]{64}$/.test(supplied)) throw new Error('Invalid webhook signature');
const expected = createHmac('sha256', secret).update(`${timestampText}.`).update(rawBody).digest();
if (!timingSafeEqual(expected, Buffer.from(supplied, 'hex'))) throw new Error('Invalid webhook signature');
return JSON.parse(rawBody.toString('utf8'));
}Note
Configure your framework to provide the original request bytes. Re-serializing parsed JSON changes the signed bytes. Keep your clock synchronized and never expose the signing secret in frontend code.
Delivery and retries
Persist the event to your own durable inbox, then return a 2xx response promptly. Requests time out after five seconds. Every failed HTTP response, redirect, timeout, or network error retries after 1 minute, 5 minutes, and 30 minutes, for four attempts total. Changes are queued as they are processed; delivery timing depends on queue backlog.
Note
Delivery is at least once and may arrive out of order. Deduplicate using event.id across retries, which keep the original event body and ID and receive a fresh signature timestamp.
Pausing or removing an endpoint stops its queued deliveries; requests already in flight may finish. Secret rotation immediately affects future attempts, so update your receiver promptly.
Note
Only public HTTPS hostnames are supported, and redirects are not followed.
Event logs
The portal's Events list keeps 30 days of events sent to your endpoints, filterable by status and event type. Select an event to inspect its payload and HTTP attempt history, including response codes, response excerpts, and durations.
Note
Removing an endpoint also removes its event history.