Docs

Manager API Reference

Detailed endpoint reference for strategies, trade submission, status, and utility APIs.

Managerv1.1Updated Sep 9, 2026

Need the source file for an AI workflow or offline reference? The raw markdown remains available at /docs/TRADELOCK_MANAGER_API_REFERENCE.md.

TradeLock Manager API Guide

Audience: Strategy managers and integration engineers
Use case: Server-to-server strategy management and trade submission
Version: 1.1

Last updated: 2026-09-09

This guide is a production-focused API reference for TradeLock manager workflows.

TradeLock verification access is free within reasonable usage limits.

1) Base URLs

  • Production: https://tradelock.net/api

This is the common prefix for API endpoints, not a submission endpoint by itself. The canonical submission URL is https://tradelock.net/api/v1/signals. All examples below use production URLs.

2) Authentication

TradeLock supports two auth methods on manager endpoints:

  1. API key (recommended for servers)
  • Header: X-Trader-Api-Key: <API_KEY>
  1. Firebase ID token
  • Header: Authorization: Bearer <FIREBASE_ID_TOKEN>

API key lifecycle endpoints

These endpoints require Firebase ID token auth (not API key auth):

  • POST /generate-api-key
  • GET /list-api-keys
  • POST /delete-api-key
  • POST /revoke-api-key (same behavior as delete)

The manager app can also retrieve configured webhook relay details with GET /relay-access. This Firebase-ID-token-only endpoint returns a private, non-cacheable response containing the relay URL and shared relay key. Never expose that response in client-side logs, public alerts, or source code.

API key scopes

generate-api-key supports key scopes:

  • full (default): can access all strategies
  • sandbox: can only submit to strategies marked as sandbox/test

A sandbox key calling a non-sandbox strategy returns:

  • 403 with code: "SANDBOX_SCOPE_RESTRICTION"

Use this section as the complete contract for building a live signal client.

Endpoint:

POST https://tradelock.net/api/v1/signals

Required headers:

http
Content-Type: application/json
X-Trader-Api-Key: <API_KEY>

3.1 Resolve the strategy ID

For strategies created in the current TradeLock app, id is the same text as strategy_name. Clients should still read the strategy list and use the exact id value:

bash
curl "https://tradelock.net/api/strategies?limit=100" \
  -H "X-Trader-Api-Key: <API_KEY>"

Relevant response fields:

json
{
  "strategies": [
    {
      "id": "Momentum Core",
      "strategy_name": "Momentum Core"
    }
  ]
}

Use strategies[].id as strategy_id in every canonical signal.

3.2 Resolve the symbol

Open New Trade in the manager app, type the ticker, and wait for TradeLock to confirm the listing. API clients can perform the same lookup with:

bash
curl "https://tradelock.net/api/symbol-search?q=SPY&limit=8&include_quotes=false" \
  -H "X-Trader-Api-Key: <API_KEY>"

Copy the exact canonical_symbol from the selected result. U.S. stocks and ETFs normally use a bare ticker. For example, SPY:ARCA is useful as a New Trade lookup hint, but the submitted canonical symbol is SPY.

European stocks and ETFs are not supported for canonical live submission yet.

3.3 Envelope fields

The request body is strict: do not add fields that are not listed here.

FieldTypeRequiredMeaning
protocol_versionstringYesMust be exactly "1.0"
strategy_idstringYesExact id returned by GET /strategies; maximum 160 characters
idempotency_keystringYesStable identifier for this intent; 1–120 characters
payloadobjectYesExactly one of the three payload shapes below
effective_atISO 8601 stringNoIntended effective time including Z or a numeric timezone; TradeLock assigns acceptance time when omitted
metadataJSON objectNoClient context such as source or tags; never put credentials here

The complete body is limited to 256 KiB. Symbols and identifiers must not have surrounding whitespace.

3.4 Target portfolio payload

Use scope: "full" to replace the complete target set. Any current holding omitted from a full target is treated as target 0%. Use scope: "patch" to change only the supplied symbols. Each weight_pct is a final portfolio target, not an order size. Negative values represent short targets and values above 100 represent leverage.

bash
curl -X POST "https://tradelock.net/api/v1/signals" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "protocol_version": "1.0",
    "strategy_id": "<STRATEGY_DOCUMENT_ID>",
    "idempotency_key": "signal-portfolio-20260909-001",
    "payload": {
      "type": "target_portfolio",
      "scope": "full",
      "targets": [
        { "symbol": "SPY", "weight_pct": 60 },
        { "symbol": "TLT", "weight_pct": 40 }
      ]
    }
  }'

targets is an array of unique canonical symbols and is limited to 500 items.

3.5 Percent-of-NAV order payload

This expresses an order size, not a final target. If manager NAV is $100,000, percent_of_nav: 5 records an order to buy or sell approximately $5,000 of the instrument.

json
{
  "protocol_version": "1.0",
  "strategy_id": "<STRATEGY_DOCUMENT_ID>",
  "idempotency_key": "signal-percent-buy-20260909-001",
  "payload": {
    "type": "percent_order",
    "symbol": "AAPL",
    "side": "buy",
    "percent_of_nav": 5
  }
}

side must be buy or sell. percent_of_nav must be greater than 0 and no greater than 10000.

3.6 Quantity order payload

Use this for an explicit number of shares or units.

json
{
  "protocol_version": "1.0",
  "strategy_id": "<STRATEGY_DOCUMENT_ID>",
  "idempotency_key": "signal-quantity-buy-20260909-001",
  "payload": {
    "type": "quantity_order",
    "symbol": "AAPL",
    "side": "buy",
    "quantity": 10
  }
}

quantity must be a positive finite number.

3.7 Acceptance, retries, and errors

A new durable event returns 201 Created:

json
{
  "event_id": "evt_...",
  "strategy_sequence": 42,
  "received_at": "2026-09-09T12:00:00Z",
  "envelope_hash": "sha256:...",
  "idempotency_key": "signal-portfolio-20260909-001"
}

Store all five receipt fields. If a request times out, retry the identical body with the same idempotency_key. Do not submit the intent to another endpoint. An identical replay returns 200 and the original receipt. Reusing a key with different content returns 409 IDEMPOTENCY_PAYLOAD_CONFLICT.

Common failures:

  • 400 INVALID_SIGNAL_ENVELOPE: wrong field, type, symbol, timestamp, or protocol version
  • 401 AUTHENTICATION_REQUIRED: missing or invalid credentials
  • 403 SANDBOX_SCOPE_RESTRICTION: sandbox key used with a non-sandbox strategy
  • 404 STRATEGY_NOT_FOUND: strategy_id does not match an owned strategy id
  • 409 STRATEGY_ARCHIVED: the strategy is archived
  • 409 IDEMPOTENCY_PAYLOAD_CONFLICT: key reused with changed content
  • 413 SIGNAL_CAPACITY_EXCEEDED: request exceeds 256 KiB or 500 targets
  • 422 MANAGER_NAV_UNAVAILABLE: TradeLock cannot capture the strategy value required at acceptance

Appendix A) Legacy verification-only submission modes

These endpoints remain available for existing clients but never publish to HUB subscribers:

  1. POST /set-target-portfolio
  2. POST /logTrade with quantity + explicit side
  3. POST /logTrade with single-symbol allocation_percent and no side

Default examples below are intentionally prefilled for sandbox testing:

  • strategy: Sandbox Strategy
  • API key header: X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>

A.1 Set full target portfolio

Behavior:

  • Full-portfolio scope by default.
  • If a current holding symbol is omitted from targets, its target is 0%.
bash
curl -X POST "https://tradelock.net/api/set-target-portfolio" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "strategy": "Sandbox Strategy",
    "targets": { "SPY": 33, "TLT": 33, "EEM": 10, "GSY": 15 },
    "execution_anchor": "next_us_equity_open",
    "open_delay_minutes": 2,
    "idempotency_key": "signal-portfolio-20260308-001"
  }'

A.2 Quantity mode: explicit buy/sell units

bash
curl -X POST "https://tradelock.net/api/logTrade" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "user_strategy_name": "Sandbox Strategy",
    "asset": "AAPL:NASDAQ",
    "trade_type": "buy",
    "quantity": 10,
    "idempotency_key": "signal-qty-buy-20260308-001"
  }'

A.3 Single-symbol target allocation (leave others unchanged)

bash
curl -X POST "https://tradelock.net/api/logTrade" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "user_strategy_name": "Sandbox Strategy",
    "asset": "AAPL:NASDAQ",
    "allocation_percent": 30,
    "idempotency_key": "signal-single-target-20260308-001"
  }'

4) Legacy request conventions

Strategy identity

A strategy is keyed by strategy_name in the manager workspace.

  • Create is effectively an upsert on strategy_name
  • Renaming an existing strategy name is not supported on update

Symbol/asset format

Trade assets are expected as uppercase symbols, with optional exchange hint:

  • AAPL
  • AAPL:NASDAQ
  • BTC-USD

For equity and ETF submissions, a bare ticker is explicitly interpreted in the U.S. namespace. Qualify non-U.S. instruments with a supported exchange, such as VOD:LSE. Whitespace around the field and : separator is trimmed, but whitespace inside a symbol or exchange token is rejected.

If more than one of asset, symbol, and ticker is supplied, every value must resolve to the same canonical instrument. Conflicting values return 400 AMBIGUOUS_ASSET_INPUT; unsupported exchange hints return 400 UNSUPPORTED_EXCHANGE.

Time and timestamps

Responses may contain:

  • ISO timestamps (string)
  • Unix epoch milliseconds (number)

Idempotency (write safety)

Use idempotency on all write operations.

Accepted forms:

  • Header: Idempotency-Key: <key>
  • Body: idempotency_key

For quick-trade, client_order_id is accepted and mapped to idempotency_key.

Validation and behavior:

  • Length must be 8..200 chars
  • Same key + same payload: replay prior response
  • Same key + different payload: 409 conflict
  • Same key while first request is processing: 409

For set-target-portfolio, same-key retries return the existing durable acceptance and rebalance_id when available.

Note for browser clients: pass idempotency in the JSON body. CORS allow-list does not include Idempotency-Key.

Fill policy and delay

For quick-trade and logTrade:

  • fill_policy: immediate_if_open (default) or next_open_with_delay
  • open_delay_minutes: non-negative number

Delay precedence:

  1. open_delay_minutes in request
  2. client default delay
  3. global fallback (5 minutes)

Live vs CSV-import fields

For live trades (import_source omitted or live), do not send:

  • reported_date
  • user_reported_price
  • reference_price
  • allocation_reference_price

These are only valid for import_source: "csv_import".

5) Error Model

Most errors use:

json
{ "error": "Human-readable message", "code": "OPTIONAL_MACHINE_CODE" }

Common statuses:

  • 200 OK
  • 201 Created (strategy created)
  • 202 Accepted (trade queued or durable rebalance job stored)
  • 400 Validation error
  • 401 Authentication failed/missing
  • 403 Authenticated but not allowed
  • 404 Not found
  • 405 Method not allowed
  • 409 Idempotency conflict/in-progress
  • 500 Internal error
  • 502 Upstream provider failure (symbol search)

6) Endpoint Reference

6.1 API Keys

POST /generate-api-key

Generate a new manager API key.

Auth:

  • Authorization: Bearer <FIREBASE_ID_TOKEN>

Request body:

FieldTypeRequiredNotes
key_namestringNoDefault: "My API Key"
client_idstringNoDefault: current user UID
key_scopestringNofull (default) or sandbox

Success (200):

json
{
  "api_key": "uuid",
  "key_id": "uuid",
  "scope": "full",
  "message": "API Key generated successfully"
}

GET /list-api-keys

List API keys for authenticated user.

Auth:

  • Authorization: Bearer <FIREBASE_ID_TOKEN>

Success (200):

json
[
  {
    "id": "key-id",
    "name": "Webhook Prod",
    "created_at": "2026-02-20T15:00:00.000Z",
    "last_used": "2026-02-27T10:11:12.000Z",
    "status": "active",
    "scope": "full"
  }
]

POST /delete-api-key (or POST /revoke-api-key)

Delete/revoke an API key.

Auth:

  • Authorization: Bearer <FIREBASE_ID_TOKEN>

Request body:

FieldTypeRequired
api_key_idstringYes

Success (200):

json
{ "message": "API Key deleted successfully" }

GET /relay-access

Return the configured TradingView/no-code relay endpoint and shared gate key.

Auth:

  • Authorization: Bearer <FIREBASE_ID_TOKEN> only

Success (200):

json
{
  "relay_endpoint": "https://tradelock-tv-relay.tradelock.workers.dev/webhook/tradingview",
  "shared_relay_key": "private-value",
  "label": "For TradingView, Zapier, Make, n8n, and similar webhook tools"
}

The response is marked private, no-store. A missing server-side relay key returns 503.

6.2 Strategy Management

POST /ensure-sandbox-onboarding

Create or reuse the authenticated manager's sandbox strategy. This is useful for first-run integration setup and requires an API key or Firebase ID token.

Success (200) returns the sandbox strategy name and whether it was created:

json
{
  "message": "Sandbox onboarding is ready.",
  "sandbox": {
    "strategy_name": "Sandbox Strategy",
    "strategy_created": true,
    "key_created": false,
    "sandbox_api_key": null,
    "sandbox_api_key_preview": null
  }
}

POST /reset-sandbox

Delete trades, daily snapshots, and pending trades belonging to the authenticated manager's sandbox strategy, then reset its verification state. This is destructive and requires an API key or Firebase ID token. It returns 404 when no sandbox strategy exists.

GET /client-config

Read the effective manager client settings used by delayed execution and allocation rebalances.

Auth:

  • API key or Firebase ID token

Optional query parameter: client_id. When omitted, the authenticated client identity is used.

Success (200):

json
{
  "client_id": "manager-client-id",
  "default_open_delay_minutes": 2,
  "effective_default_open_delay_minutes": 2,
  "min_rebalance_notional_usd": 25,
  "effective_min_rebalance_notional_usd": 25,
  "source": "client",
  "exists": true
}

POST|PUT|PATCH /client-config/update

Update one or both client settings. The body must include at least one of default_open_delay_minutes or min_rebalance_notional_usd; both values must be non-negative numbers. Auth is API key or Firebase ID token.

Optional client_id may be supplied in the body or query string. Success (200) returns the updated values.

GET /strategies

List strategies for authenticated manager.

Auth:

  • API key or ID token

Query params:

ParamTypeRequiredNotes
limitnumberNo1..100, default 50
page_tokenstringNoLast returned strategy ID
include_archivedboolean-likeNotrue/1/yes to include soft-deleted

Success (200):

json
{
  "strategies": [
    {
      "id": "Momentum Core",
      "strategy_name": "Momentum Core",
      "access_mode": "workspace",
      "hub_status": "not_requested",
      "tags": ["momentum"],
      "asset_types": ["equity"],
      "initial_capital_usd": 100000
    }
  ],
  "next_page_token": null
}

Notes:

  • When OpenTimestamps verification anchors are enabled and the current account is eligible to see them, strategies may also include verification_anchor_latest_v1.
  • verification_anchor_latest_v1 is the latest per-strategy anchor summary and includes:
  • provider: opentimestamps
  • snapshot_date
  • status: pending, attested, or error
  • anchor_path and proof_path
  • anchor_sha256
  • stamped_at, upgraded_at, attested_at, last_error

Example:

json
{
  "verification_anchor_latest_v1": {
    "provider": "opentimestamps",
    "snapshot_date": "2026-04-04",
    "status": "pending",
    "anchor_sha256": "f8f8...",
    "anchor_path": "verification/ots/.../anchor.json",
    "proof_path": "verification/ots/.../anchor.json.ots"
  }
}

GET /verification-anchor-file

Download the latest OpenTimestamps anchor artifact for one of your strategies.

Auth:

  • ID token

Query params:

ParamTypeRequiredNotes
strategyName or strategy_namestringYesTarget strategy
kindstringYesanchor or proof

Behavior:

  • kind=anchor returns the latest anchor.json.
  • kind=proof returns the latest detached anchor.json.ots proof.
  • Returns 404 when the strategy has no anchor yet, the feature is disabled, or the current account is not eligible to view anchor files.

Success:

  • 200 with an attachment response
  • Content type is application/json for anchor
  • Content type is application/octet-stream for proof

POST /createStrategy

Create or update a strategy by strategy_name.

Auth:

  • API key or ID token

Request body:

FieldTypeRequiredNotes
strategy_namestringYesStrategy ID in workspace
descriptionstringNo
public_summarystringNoDefaults to description if missing
access_modestringNoworkspace, shareable, or invite_only; default workspace
hub_statusstringNoProviders may use not_requested or under_review; listed is admin-managed
public_slugstringNoApplied when access_mode=shareable
tagsstring[]NoUp to 25 entries after normalization
asset_typesstring[]NoUp to 25 entries after normalization
metadataobjectNoPrimitive/object values only
initial_capital_usdnumberNoMust be positive if provided
trade_hide_daysnumberNoDays of recent trades hidden from public viewers. Allowed: 0 (show all), 30, 60, 90, 9999 (hide all). Default: 0
min_rebalance_move_pctnumberNoDrift filter for set-target-portfolio. Legs with a notional move below this % of running capital are skipped as no-ops. Range 0100. Default: 1

Success:

  • 201 when created
  • 200 when existing strategy is updated
json
{
  "message": "Strategy created successfully",
  "created": true,
  "strategy": {
    "id": "Momentum Core",
    "strategy_name": "Momentum Core",
    "is_deleted": false
  }
}

POST|PUT|PATCH /update-strategy (alias: /updateStrategy)

Patch strategy fields.

Auth:

  • API key or ID token

Request body:

FieldTypeRequiredNotes
strategy_id or strategy_namestringYesTarget strategy
descriptionstringNo
public_summarystringNo
access_modestringNoworkspace, shareable, or invite_only
hub_statusstringNonot_requested or under_review; listed is admin-managed
public_slugstringNoRegenerated/sanitized when needed
tagsstring[]No
asset_typesstring[]No
metadataobjectNo
is_deletedbooleanNoSoft-delete toggle
initial_capital_usdnumberNoMust be positive
trade_hide_daysnumberNoDays of recent trades hidden from public viewers. Allowed: 0, 30, 60, 90, 9999.
min_rebalance_move_pctnumberNoDrift filter threshold 0100.

Constraints:

  • Renaming strategy name is not supported (400)
  • verification_rate is server-managed and cannot be set

Success (200):

json
{
  "message": "Strategy updated successfully",
  "strategy": {
    "id": "Momentum Core",
    "strategy_name": "Momentum Core"
  }
}

POST|DELETE /delete-strategy (alias: /deleteStrategy)

Soft-delete (archive) a strategy. Archiving also cancels active pending trades for the same strategy before the archive is applied. Archived strategies are preserved indefinitely and can be restored or permanently deleted from the owner's account.

Auth:

  • API key or ID token

Request body:

FieldTypeRequired
strategy_id or strategy_namestringYes

Success (200):

json
{
  "message": "Strategy archived successfully",
  "strategy_id": "Momentum Core",
  "deleted": true,
  "pending_trade_cancellation": {
    "matched_pending_trades": 1,
    "cancelled_count": 1,
    "already_cancelled_count": 0,
    "processing_count": 0,
    "not_cancellable_count": 0,
    "failed_count": 0
  }
}

Conflict (409):

  • Returned as ARCHIVE_BLOCKED_BY_PENDING_TRADES if one or more matching pending trades are already processing or could not be cancelled safely.

POST|DELETE /permanent-delete-strategy (alias: /permanentDeleteStrategy)

Permanently delete an archived strategy and its strategy-owned data. This removes the strategy document tree, matching pending trades, immutable trade storage files, verification snapshot files, and OpenTimestamps anchor/proof files tied to that strategy.

Auth:

  • ID token only

Request body:

FieldTypeRequired
strategy_id or strategy_namestringYes
confirmation_textstringYes; must exactly equal the strategy ID
target_user_idstringAdmin only; omit when deleting your own strategy

Success (200):

json
{
  "message": "Strategy permanently deleted",
  "strategy_id": "Momentum Core",
  "deleted": true,
  "permanent": true
}

Conflict (409):

  • Returned as STRATEGY_NOT_ARCHIVED if the strategy has not been archived first.

API-key authentication is rejected for permanent deletion. Retrying a completed deletion returns success with already_deleted: true.

GET /strategy-audit-pack

Download a portable strategy audit ZIP that can be verified without TradeLock, Firestore, credentials, npm, or network access.

Auth:

  • ID token or full-access API key

Query parameters:

FieldTypeRequired
strategyName or strategy_idstringYes

Success (200) returns application/zip. Extract the archive and run node verify.cjs . with Node.js 18 or newer.

6.3 Trade Validation and Submission

POST /validate-trade (alias: POST /dry-run-trade)

Validate and normalize a trade request without creating a trade.

Auth:

  • API key or ID token

Key inputs:

  • Strategy: strategy or strategy_name or user_strategy_name (optional if user has exactly one strategy)
  • Asset: ticker or symbol or asset
  • Side: trade_type or side or action (buy|sell)
  • Size: exactly one of quantity or allocation aliases
  • Optional check_market_data: true resolves the exact listing and reports executable or closed without writing a trade. Other quote failures return a stable code.

Allocation aliases:

  • allocation_percent
  • allocation_pct
  • allocationPercentage
  • allocation%

Success (200):

json
{
  "valid": true,
  "mode": "validate_only",
  "key_scope": "full",
  "normalized_payload": {
    "user_strategy_name": "Momentum Core",
    "asset": "AAPL",
    "trade_type": "buy",
    "allocation_percent": 10,
    "fill_policy": "immediate_if_open"
  },
  "notes": [
    "Allocation mode with trade_type sizes this order from strategy running capital."
  ]
}

POST /quick-trade

Manager-friendly trade submission with broad aliases.

Recommendation:

  • Keep this only for backwards-compatible verification clients or historical adapters.
  • New live integrations should publish exactly once to POST /v1/signals.

Auth:

  • API key or ID token

Request fields:

Canonical fieldAccepted input keysRequiredNotes
strategystrategy, strategy_name, user_strategy_nameConditionallyOptional only if exactly one strategy exists
assetticker, symbol, assetYesSYMBOL or SYMBOL:EXCHANGE
trade sidetrade_type, side, actionDependsRequired when using quantity
quantityquantity, qtyDependsMutually exclusive with allocation
allocationallocation_percent, allocation_pct, allocationPercentage, allocation%DependsMutually exclusive with quantity
idempotencyidempotency_key, client_order_idRecommendedUse in production
executionfill_policy, open_delay_minutesNo
tagstagsNostring[]

Behavior:

  • Calls logTrade internally after normalization
  • Returns immediate fill (200) or queued (202)
  • Pending replacement rules for same strategy + asset:
  • target_allocation mode (send allocation_percent and omit side) replaces the existing active pending target-allocation intent for that symbol.
  • quantity mode and side-based allocation_percent mode append as separate pending orders (FIFO).

POST /set-target-portfolio

Asynchronous contract: idempotency_key (or client_order_id) is required. The endpoint returns 202 Accepted with a durable rebalance_id; poll GET /rebalance-status?rebalance_id=... for completed, partially_completed, or failed. A timeout is an unknown outcome—retry the identical payload with the same key. Only one rebalance may be active per strategy.

Set an authoritative target portfolio for a strategy.

  • Full-portfolio semantics are default: symbols with open positions that are not in targets are auto-targeted to 0%.
  • Before submitting new legs, the endpoint cancels all cancellable pending trades for the same strategy by default.
  • Trade legs are submitted sequentially (sells first, then buys).
  • Each leg is submitted as target-allocation logic (allocation_percent without side).
  • Targets do not need to sum to 100. Totals below 100 leave residual cash uninvested. For example, an 80 total target is valid and leaves 20% in cash.
  • Legs whose notional move is smaller than min_rebalance_move_pct (set on the strategy, default 1%) are silently skipped. The response marks them with drift_filtered: true and drift_pct.

Pricing and sizing behavior:

  • set-target-portfolio sizing uses last-price lookup (getLastPrice) for each leg, not Alpaca execution quotes.
  • Provider order for last-price lookup:
  • Crypto-like symbols: coinbase -> twelve-data -> yahoo-finance2 -> alpha-vantage
  • Futures-like symbols: twelve-data -> yahoo-finance2 -> alpha-vantage -> coinbase
  • Other symbols: configured default provider first, then remaining providers.
  • Configured default provider:
  • twelve-data when TWELVEDATA_API_KEY or TWELVE_DATA_API_KEY is present
  • else alpha-vantage when ALPHA_VANTAGE_API_KEY is present
  • else yahoo-finance2
  • If no provider returns a usable quote, the endpoint returns 400 with a sizing error per symbol.

Auth:

  • API key or ID token

Request fields:

FieldTypeRequiredNotes
strategy / strategy_name / user_strategy_namestringConditionallyOptional only if exactly one strategy exists
targetsarray or object mapYesArray: [{ "symbol": "SPY", "allocation_percent": 33 }] or map: { "SPY": 33 }
execution_anchorenumNonow (default) or next_us_equity_open
execute_on_stock_openbooleanNoAlias for execution_anchor=next_us_equity_open
open_delay_minutesnumberNoApplied per leg
cancel_pending_trades_firstbooleanNoDefault true; set false to preserve the current pending queue
dry_runbooleanNoIf true, returns plan only (no submissions)
idempotency_key / client_order_idstringYesRequired; timeout retries must reuse the identical payload and key
tagsstring[]NoApplied to each submitted leg

Example:

json
{
  "strategy": "Sandbox Strategy",
  "targets": {
    "SPY": 33,
    "TLT": 33,
    "EEM": 10,
    "GSY": 15
  },
  "execution_anchor": "next_us_equity_open",
  "open_delay_minutes": 2,
  "idempotency_key": "stp-20260308-001"
}

Acceptance (202) returns after the durable job is created:

json
{
  "message": "Rebalance accepted for asynchronous processing.",
  "rebalance_id": "stp_1773763355292_ganlirnm",
  "strategy": "Sandbox Strategy",
  "status": "accepted",
  "status_url": "/api/rebalance-status?rebalance_id=stp_1773763355292_ganlirnm"
}
  • Same key and identical payload returns the same rebalance_id.
  • Same key with a different payload returns 409 IDEMPOTENCY_PAYLOAD_CONFLICT.
  • Only one rebalance can be active per strategy. Another returns 409 REBALANCE_ALREADY_ACTIVE with active_rebalance_id.
  • Pricing, sizing, pending-trade cancellation, and leg submission happen in the durable worker.
  • A timeout is an unknown outcome. Retry the identical payload and key; do not submit a replacement rebalance.

GET /rebalance-status

Get the durable state and result of an accepted portfolio rebalance.

Auth: API key or ID token. The rebalance must belong to the authenticated trader.

ParamRequiredNotes
rebalance_id / rebalanceIdYesID returned by set-target-portfolio

States:

  • accepted: durable job stored
  • planning: worker is pricing, sizing, or submitting legs
  • completed: all required legs were submitted or no trades were needed
  • partially_completed: at least one leg succeeded before a later failure
  • failed: job did not complete successfully

Terminal responses include result with the summary, planned and processed legs, trade or pending-trade IDs, and error details.

Client flow:

  1. Persist the payload and idempotency key before submission.
  2. On 202, persist rebalance_id.
  3. Poll until completed, partially_completed, or failed.
  4. If the connection is lost before 202, retry the identical submission with the same key.
  • When cancel_pending_trades_first is enabled, responses also include preflight_pending_trade_reset.
  • If a pending trade is already processing, or cancellation fails during preflight, the job reaches failed before submitting new legs.

Example response fragment:

json
{
  "rebalance_id": "stp_1773763355292_ganlirnm",
  "strategy": "Sandbox Strategy",
  "status": "completed",
  "result": {
    "message": "Target portfolio submitted successfully.",
    "summary": {
      "immediate_count": 0,
      "queued_count": 4,
      "no_op_count": 0
    }
  }
}

POST /logTrade

Advanced trade endpoint.

Auth:

  • API key or ID token

Core fields:

FieldTypeRequiredNotes
user_strategy_name / strategy_name / strategystringConditionallyOptional only if exactly one strategy exists
assetstringYesSYMBOL or SYMBOL:EXCHANGE
trade_type / side / actionbuy|sellDependsRequired for direct quantity mode
quantity / qtynumberDependsRequired unless allocation is provided
allocation_percent and aliasesnumberDependsRequired unless quantity is provided
fill_policyenumNoimmediate_if_open (default) or next_open_with_delay
open_delay_minutesnumberNonon-negative
idempotency_keystringRecommendedor Idempotency-Key header
tagsstring[]No

Sizing modes:

  1. Quantity mode
  • send quantity + explicit side
  1. Allocation-percent mode
  • send allocation_percent + side
  • quantity is computed from strategy running capital
  1. Target-allocation mode
  • send allocation_percent and omit side
  • endpoint computes target position delta and direction
  • queued submissions in this mode replace prior active pending target-allocation intent for the same strategy+asset

CSV import-only fields (import_source: "csv_import"):

FieldTypeNotes
import_sourcestringMust equal csv_import
reported_dateISO-8601 stringOptional
user_reported_pricenumberRequired and must be > 0
reference_pricenumberOptional (sizing reference)
allocation_reference_pricenumberOptional (sizing reference)

Optional enrichment fields:

  • rankable_strategy_id, isin, tick_size, multiplier, point_value, margin, commission

Live execution quote behavior:

  • logTrade uses execution quote lookup (getExecutionQuote) when processing non-CSV trades.
  • U.S. equity/ETF execution uses Tradier -> Alpaca -> EODHD by default; eToro is reserved for exchange-qualified non-U.S. equities/ETFs.
  • EODHD-style EU symbols submitted by StrategyLab are preserved as source identifiers and may resolve to an equivalent eToro-supported cross-listing. Audit fields record the source symbol, resolved quote symbol, quote exchange, and ISIN when available; ambiguous mappings fail closed.
  • Alpaca remains opt-in through ALPACA_EQUITY_ENABLED; Tradier is independently opt-in through TRADIER_EQUITY_ENABLED. Merely configuring TRADIER_ACCESS_TOKEN does not activate it.
  • Alpaca and Tradier quotes use top-of-book side pricing for U.S. listings: buy -> ask, sell -> bid. eToro is reserved for exchange-qualified non-U.S. listings. Stale/crossed/missing side quotes are rejected.
  • If an execution-quote provider fails, the next enabled provider is tried. If all fail, execution falls back to last-price providers.
  • Fallback executions are marked with execution_basis: "last" and include quote_fallback_reason.
  • If no quote is available (or market is not open), the trade is queued (202) for delayed processing; failed delayed attempts can move to error_processing.

Example provider configuration:

env
ALPACA_EQUITY_ENABLED=true
TRADIER_EQUITY_ENABLED=true
TRADIER_ACCESS_TOKEN=your-production-token
EXECUTION_QUOTE_PROVIDER_ORDER=tradier,alpaca

Tradier uses the production consolidated feed. TRADIER_API_KEY is accepted as an alias for TRADIER_ACCESS_TOKEN. Its sandbox feed is delayed and is not used by this integration.

Success patterns:

Immediate/logged (200):

json
{
  "message": "Trade logged successfully!",
  "tradeId": "abc123",
  "execution": {
    "fill_policy": "immediate_if_open",
    "resolved_open_delay_minutes": 5
  },
  "sizing": {
    "mode": "allocation_percent",
    "allocation_percent": 10,
    "quantity": 42
  },
  "hybrid": {
    "verified": true,
    "storageRef": "strategies/.../snapshot.json",
    "hash": "..."
  },
  "price_data": {
    "price": 201.12,
    "market_state": "REGULAR"
  }
}

Queued (202):

json
{
  "message": "Trade received and queued for processing when market opens.",
  "pendingTradeId": "pnd_456",
  "current_market_state": "CLOSED",
  "execution": {
    "fill_policy": "next_open_with_delay",
    "waiting_for_next_market_session": false,
    "resolved_open_delay_minutes": 5
  }
}

No-op target allocation (200):

json
{
  "message": "No trade created. Strategy is already at the requested target allocation.",
  "no_op": true
}

6.4 Trade Status and History

GET /trade-status

Get lifecycle/timeline for a trade or pending trade.

Auth:

  • API key or ID token

Query params:

ParamRequiredNotes
strategyName or strategy_nameYesStrategy name
tradeId or trade_idYestradeId or pendingTradeId from submit response

Success (200) returns timeline-style response with:

  • source: pending_trades or strategy_trade
  • current_status: e.g. queued, pending_delay, filled, immutable_stored, verified, error_*
  • snapshot_freshness: not_ready, fresh, stale, unknown
  • execution, filled, timeline

GET /strategy-trades

Get strategy trade history (and optionally pending queue).

Auth:

  • API key or ID token

Query params:

ParamRequiredNotes
strategy_nameYesStrategy name
limitNoDefault 100
include_pendingNotrue/1/yes to include active pending trades

Success (200):

  • Without pending: { "trades": [...] }
  • With pending: { "trades": [...], "pending_trades": [...] }

POST /cancel-pending-trade

Cancel one queued pending trade before execution.

Auth:

  • API key or ID token

Request body:

FieldTypeRequiredNotes
strategy_namestringYesStrategy name
pending_trade_idstringYesID from queued trade response
reasonstringNoOptional cancellation note

Success (200):

json
{
  "message": "Pending trade cancelled successfully.",
  "pending_trade_id": "pnd_456",
  "strategy_name": "Sandbox Strategy",
  "status": "cancelled"
}

Possible conflicts:

  • 409 if order is already processing or in another non-cancellable status
  • 404 if pending trade does not belong to caller/strategy

POST /cancel-all-pending-trades

Cancel all cancellable pending trades for one strategy.
set-target-portfolio now performs this queue reset automatically by default, so use this endpoint when you want to clear the queue explicitly without submitting a new target portfolio.

Auth:

  • API key or ID token

Request body:

FieldTypeRequiredNotes
strategy_namestringYesStrategy name to clear
reasonstringNoOptional cancellation note applied to all cancelled rows

Success (200):

json
{
  "message": "Cancelled 3 pending trade(s) for strategy 'Sandbox Strategy'.",
  "strategy_name": "Sandbox Strategy",
  "matched_pending_trades": 5,
  "cancelled_count": 3,
  "already_cancelled_count": 1,
  "processing_count": 1,
  "not_cancellable_count": 0,
  "failed_count": 0
}

6.5 Market Data Utilities

GET|POST /symbol-search

Lookup symbols across providers.

Default routing aggregates Twelve Data, Yahoo Finance, and configured Alpha Vantage metadata. An exact U.S. listing is returned without contacting eToro. When no exact U.S. listing exists and metadata identifies a supported European exchange, TradeLock queries eToro for live-supported listings. Alpaca and Tradier are used for quote confirmation, not text search. Supplying provider still forces that search provider.

Auth:

  • Required
  • Send either:
  • X-Trader-Api-Key: <API_KEY>
  • Authorization: Bearer <FIREBASE_ID_TOKEN>

Inputs:

FieldTypeRequiredNotes
qstringYesquery text
providerstringNoyahoo-finance2, alpha-vantage, twelve-data, coinbase, eodhd, etoro
limitnumberNodefault 10, max 25
exchangestringNorequested TradeLock exchange code
symbologystringNotradelock, eodhd, or etoro; enables namespace-specific suffix parsing
include_quotesbooleanNodefaults to true for compatibility; use false while typing

Success (200):

json
{
  "query": "AAPL",
  "count": 1,
  "provider_used": "twelve-data",
  "attempts": [
    { "provider": "twelve-data", "status": "success" }
  ],
  "results": [
    {
      "symbol": "AAPL",
      "canonical_symbol": "AAPL",
      "name": "Apple Inc",
      "exchange": "NASDAQ",
      "type": "Common Stock",
      "provider": "twelve-data"
    }
  ]
}

If all providers fail: 502 with provider attempt details.

Legacy European symbol lookup (not available for canonical live submission):

  1. Search the source symbol, for example q=LQQ.PA&symbology=eodhd&include_quotes=false.
  2. Select the exact returned canonical_symbol. Alternative name matches are marked unverified_name and require explicit selection.
  3. Submit that canonical symbol unchanged in one /v1/signals envelope.

European stocks and ETFs are not supported for canonical live submission yet. Historical-data coverage does not imply that the exact listing has a live eToro quote. Bare tickers such as SPY, TLT, EEM, and AAPL retain the existing U.S. behavior and do not require a prior search.

Market-data failures use stable codes: INSTRUMENT_UNRESOLVED, INSTRUMENT_AMBIGUOUS, UNSUPPORTED_EXCHANGE, QUOTE_CURRENCY_UNKNOWN, QUOTE_STALE, QUOTE_FUTURE_TIMESTAMP, QUOTE_UNAVAILABLE, MARKET_CLOSED, or PROVIDER_FAILURE.

GET|POST /fetch-price (alias: /fetchPrice)

Get last/current price and market state.

Auth:

  • Required
  • Send either:
  • X-Trader-Api-Key: <API_KEY>
  • Authorization: Bearer <FIREBASE_ID_TOKEN>

Inputs:

FieldTypeRequiredNotes
symbolstringYese.g. AAPL
providerstringNoprovider hint

When market is closed, endpoint can still return 200 with last available price and note.

Provider routing notes:

  • Crypto symbol normalization for Coinbase:
  • BTC/USD is normalized to BTC-USD
  • bare symbols like BTC are normalized to BTC-USD
  • Crypto symbols are routed to Coinbase first, then fallback providers.

7) Legacy verification-only cURL reference

The examples in this section document existing-client compatibility. For new live integrations, use the canonical /v1/signals examples in section 3.

1. Legacy set portfolio targets

bash
curl -X POST "https://tradelock.net/api/set-target-portfolio" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "strategy": "Sandbox Strategy",
    "targets": {
      "SPY": 33,
      "TLT": 33,
      "EEM": 10,
      "GSY": 15
    },
    "execution_anchor": "next_us_equity_open",
    "open_delay_minutes": 2,
    "idempotency_key": "signal-portfolio-20260308-001"
  }'

2. Buy by quantity

bash
curl -X POST "https://tradelock.net/api/logTrade" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "user_strategy_name": "Sandbox Strategy",
    "asset": "AAPL:NASDAQ",
    "trade_type": "buy",
    "quantity": 10,
    "idempotency_key": "signal-qty-buy-20260308-001"
  }'

3. Individual target allocation (single symbol)

bash
curl -X POST "https://tradelock.net/api/logTrade" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "user_strategy_name": "Sandbox Strategy",
    "asset": "AAPL:NASDAQ",
    "allocation_percent": 30,
    "idempotency_key": "signal-single-target-20260308-001"
  }'

4. Poll status

bash
curl "https://tradelock.net/api/trade-status?strategyName=Sandbox%20Strategy&tradeId=<TRADE_OR_PENDING_ID>" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>"

8) Integration Checklist

  1. Use one API key per integration (webhook, backend service, ETL).
  2. Read GET /strategies and use the selected strategy's exact id.
  3. Resolve the instrument with GET /symbol-search and use its exact canonical_symbol.
  4. Send one of the three strict payloads to POST /v1/signals.
  5. Create a new idempotency_key for a new intent; reuse it only to retry the identical body.
  6. Store the complete acceptance receipt.
  7. Alert on repeated 4xx responses and do not fall back to a legacy submission endpoint.
  8. Separate sandbox and production strategies and API keys.

9) Changelog

  • 1.1 (2026-09-09)
  • Made the canonical contract self-contained for human and LLM integration builders.
  • Added exact strategy ID and symbol discovery, payload constraints, receipt handling, retries, and error codes.
  • Corrected the symbol lookup route to /symbol-search and documented the current European-instrument limitation.
  • 0.8 (2026-08-16)
  • Documented the authenticated webhook relay access endpoint.
  • Documented sandbox onboarding and sandbox reset endpoints.
  • Documented client execution configuration read/update endpoints.
  • Clarified exchange qualification and conflicting asset-field behavior.
  • 0.7 (2026-07-13)
  • Documented durable asynchronous portfolio jobs and required idempotency.
  • Added GET /rebalance-status, terminal states, polling, and timeout recovery.
  • 0.6 (2026-04-22)
  • Added trade_hide_days field to createStrategy and update-strategy. Controls how many days of recent trades are hidden from public viewers (0, 30, 60, 90, 9999).
  • Added min_rebalance_move_pct field to createStrategy and update-strategy. Legs in set-target-portfolio whose notional move is below this % of running capital are skipped. Default 1.
  • Documented drift filter response fields (drift_filtered, drift_pct) in set-target-portfolio.
  • 0.5 (2026-03-17)
  • Documented set-target-portfolio preflight cancellation of existing pending trades.
  • Documented cancel_pending_trades_first=false opt-out and preflight_pending_trade_reset response field.
  • Clarified request-level replay behavior for set-target-portfolio idempotency retries.
  • 0.4 (2026-03-08)
  • Added POST /cancel-all-pending-trades for strategy-scoped queue reset.
  • Documented bulk-cancel response counters (cancelled_count, processing_count, etc.).
  • 0.3 (2026-03-08)
  • Added clear recommended submission order with set-target-portfolio as primary.
  • Added copy-paste sandbox defaults (Sandbox Strategy, <YOUR_SANDBOX_API_KEY>).
  • Clarified three manager submission modes: full portfolio targets, quantity buy/sell, and single-symbol target allocation.
  • 0.2 (2026-02-27)
  • Rewrote guide into endpoint-reference format.
  • Corrected strategy endpoint requirements and status codes.
  • Corrected auth requirements for utility endpoints.
  • Added API key lifecycle endpoints and sandbox scope behavior.
  • Clarified sizing modes, idempotency behavior, and queue semantics.
  • 0.1 (2026-02-27)
  • Initial manager-focused API guide.