# TradeLock Manager API Guide

**Audience:** Strategy managers and integration engineers

**Use case:** Account setup, strategy management, canonical signal publishing, and reporting

**Version:** 1.2

**Last updated:** 2026-09-19

TradeLock has one public submission contract for new integrations: canonical
signals. API-key and strategy-management calls remain separate, current APIs;
they are not alternative signal formats.

## 1) Base URL and route model

Production API base:

`https://tradelock.net/api`

Construct every request by appending an endpoint path to that base:

| Purpose | Method and path |
|---|---|
| Generate an API key | `POST /generate-api-key` |
| List API keys | `GET /list-api-keys` |
| Delete an API key | `POST /delete-api-key` |
| List strategies | `GET /strategies` |
| Create a strategy | `POST /createStrategy` |
| Update a strategy | `PATCH /update-strategy` |
| Archive a strategy | `POST /delete-strategy` |
| Publish canonical intent | `POST /v1/signals` |
| Check canonical processing | `GET /v1/signals?event_id=...` |
| Read strategy trades | `GET /strategy-trades` |
| Resolve a symbol | `GET /symbol-search` |

For example, canonical publication uses:

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

The `/v1` prefix versions the strict signal envelope. The unversioned
management paths predate that envelope and remain the current control-plane
API. Different paths do not imply different servers or credentials.

## 2) Authentication

TradeLock uses two primary credentials:

- API key for server-to-server strategy and signal calls:
  `X-Trader-Api-Key: <API_KEY>`
- Firebase ID token for signed-in account administration:
  `Authorization: Bearer <FIREBASE_ID_TOKEN>`

API-key lifecycle calls require a Firebase ID token. This prevents an API key
from creating, listing, or deleting account credentials. Strategy-management
calls accept an API key or Firebase ID token. Canonical signal clients should
use an API key.

API keys may have these scopes:

- `full`: may access the owner's active strategies.
- `sandbox`: may submit only to strategies marked as sandbox/test.

A sandbox key used with a normal strategy returns `403` with
`SANDBOX_SCOPE_RESTRICTION`.

## 3) Recommended integration sequence

1. Sign in to TradeLock and obtain a Firebase ID token.
2. Create an API key in the app or with `POST /generate-api-key`.
3. Create a strategy with `POST /createStrategy`, if needed.
4. Read `GET /strategies` and retain the exact strategy `id`.
5. Resolve instruments with `GET /symbol-search`.
6. Publish one canonical envelope to `POST /v1/signals`.
7. Store the acceptance receipt.
8. Poll `GET /v1/signals?event_id=...` until processing is terminal.
9. If an order is queued, follow the `trade_status_path` returned in status.

Do not retry through another submission endpoint. If the first response is
lost, resend the identical canonical envelope with the same idempotency key.

## 4) Canonical signal publishing

Endpoint:

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

Headers:

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

### 4.1 Envelope

The envelope is strict. Unexpected fields return
`400 INVALID_SIGNAL_ENVELOPE`.

| Field | Type | Required | Meaning |
|---|---|---|---|
| `protocol_version` | string | Yes | Must be exactly `"1.0"` |
| `strategy_id` | string | Yes | Exact `id` from `GET /strategies`; maximum 160 characters |
| `idempotency_key` | string | Yes | Stable ID for this intent; 1–120 characters |
| `payload` | object | Yes | One payload shape from sections 4.2–4.4 |
| `effective_at` | ISO 8601 string | No | Time the signal became effective, including timezone; acceptance time is used when omitted |
| `metadata` | object | No | Non-secret client context |

`effective_at` records signal timing. It does not schedule future execution.
The request is limited to 256 KiB and a target portfolio is limited to 500
targets.

### 4.2 Full or partial target portfolio

Use `scope: "full"` to declare the complete target portfolio. Existing symbols
omitted from a full target are targeted to `0%`. Use `scope: "patch"` to change
only the supplied symbols. Targets need not sum to 100; unused weight remains
cash.

```json
{
  "protocol_version": "1.0",
  "strategy_id": "Momentum Core",
  "idempotency_key": "portfolio-20260919-001",
  "payload": {
    "type": "target_portfolio",
    "scope": "full",
    "targets": [
      { "symbol": "SPY", "weight_pct": 60 },
      { "symbol": "TLT", "weight_pct": 40 }
    ]
  }
}
```

Each symbol must be unique and canonical. `weight_pct` is a final portfolio
target, not an order size.

### 4.3 Percent-of-NAV order

This expresses an order size. For a strategy NAV of $100,000,
`percent_of_nav: 5` represents approximately $5,000 of buy or sell intent.

```json
{
  "protocol_version": "1.0",
  "strategy_id": "Momentum Core",
  "idempotency_key": "percent-buy-20260919-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 zero and
no greater than 10000.

### 4.4 Quantity order

```json
{
  "protocol_version": "1.0",
  "strategy_id": "Momentum Core",
  "idempotency_key": "quantity-buy-20260919-001",
  "payload": {
    "type": "quantity_order",
    "symbol": "AAPL",
    "side": "buy",
    "quantity": 10
  }
}
```

`quantity` must be a positive finite number.

### 4.5 Complete publication example

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

New durable acceptance returns `201`; an identical idempotent replay returns
`200` with the original receipt:

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

Store every receipt field. Acceptance confirms durable storage, not order
completion or a market fill.

### 4.6 Status and queued orders

```bash
curl "https://tradelock.net/api/v1/signals?event_id=evt_..." \
  -H "X-Trader-Api-Key: <API_KEY>"
```

Signal status may be:

- `accepted` or `processing`: verification is still running.
- `submitted`: TradeLock derived and submitted the required orders.
- `no_op`: no order was required.
- `failed`: processing did not complete successfully.

A submitted signal may contain queued orders. Inspect every item in `orders`.
A queued item includes `pending_trade_id` and `trade_status_path`; poll that
path with the same API key until it fills or reaches another final state.

### 4.7 Idempotency and retries

- New intent: generate a new idempotency key.
- Retry of the same intent: reuse the same key and identical body.
- Same key with changed content: `409 IDEMPOTENCY_PAYLOAD_CONFLICT`.
- A timeout is an unknown outcome; retry identically rather than creating a
  replacement intent.

## 5) API-key management

These endpoints require `Authorization: Bearer <FIREBASE_ID_TOKEN>`.

### `POST /generate-api-key`

```json
{
  "key_name": "Production publisher",
  "key_scope": "full"
}
```

`key_scope` is optional and defaults to `full`; it may also be `sandbox`.
Success returns the plaintext `api_key` once, together with `key_id`, preview,
and scope. Store the key securely.

### `GET /list-api-keys`

Returns the signed-in user's key records, including ID, name, preview, scope,
status, creation time, and last-used time. It does not return key secrets.

### `POST /delete-api-key`

`POST /revoke-api-key` is an alias.

```json
{ "api_key_id": "<KEY_ID>" }
```

The caller must own the key. Success returns `200`.

### `GET /relay-access`

Returns the configured TradingView/no-code relay URL and shared relay gate key.
The response is private and non-cacheable. Never put either credential in
public code, documentation, logs, or screenshots.

## 6) Strategy management

These calls use the same API base as canonical signals. They accept an API key
or Firebase ID token unless noted otherwise.

### `GET /strategies`

Query parameters:

| Field | Required | Notes |
|---|---|---|
| `limit` | No | 1–100; default 50 |
| `page_token` | No | Last returned strategy ID |
| `include_archived` | No | `true`, `1`, or `yes` |

Use the returned `strategies[].id` as `strategy_id` in canonical envelopes.

### `POST /createStrategy`

Creates a strategy or updates the existing strategy with the same
`strategy_name`.

| Field | Required | Notes |
|---|---|---|
| `strategy_name` | Yes | Workspace strategy ID |
| `description` | No | Manager-facing description |
| `public_summary` | No | Defaults to description |
| `initial_capital_usd` | No | Positive number |
| `access_mode` | No | `workspace`, `shareable`, or `invite_only` |
| `tags` | No | String array |
| `asset_types` | No | String array |
| `metadata` | No | JSON object |
| `trade_hide_days` | No | `0`, `30`, `60`, `90`, or `9999` |
| `min_rebalance_move_pct` | No | Canonical target-portfolio drift filter, 0–100; default 1 |

```bash
curl -X POST "https://tradelock.net/api/createStrategy" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <API_KEY>" \
  -d '{
    "strategy_name": "Momentum Core",
    "description": "Monthly momentum allocation",
    "initial_capital_usd": 100000
  }'
```

Returns `201` when created and `200` when an existing strategy is updated.

### `PATCH /update-strategy`

`/updateStrategy` is an alias. Supply `strategy_id` or `strategy_name` plus the
fields to change. Strategy renaming is not supported. `verification_rate` is
server-managed.

### `POST /delete-strategy`

`/deleteStrategy` is an alias. Supply `strategy_id` or `strategy_name`.
This archives the strategy and cancels cancellable pending orders. Archived
strategies remain recoverable.

### `POST /permanent-delete-strategy`

Requires a Firebase ID token and an already archived strategy. Supply the
strategy ID and exact `confirmation_text`. This permanently removes the
strategy-owned data and cannot be undone.

### Sandbox helpers

- `POST /ensure-sandbox-onboarding`: create or reuse the manager's sandbox
  strategy.
- `POST /reset-sandbox`: destructively clear the authenticated manager's
  sandbox records and reset its verification state.

## 7) Read and utility endpoints

### `GET /strategy-trades`

Required query: `strategy_name`. Optional: `limit` and `include_pending`.
Returns completed trades and, when requested, the pending queue.

### `GET /trade-status`

Canonical signal status may return a `trade_status_path` for a queued order.
Call that exact path with the same API key. Direct construction requires
`strategyName` and `tradeId`.

### `GET /strategy-audit-pack`

Supply `strategyName` or `strategy_id`. Returns a portable ZIP that can be
verified offline with Node.js 18 or newer.

### `GET /symbol-search`

Required query: `q`. Optional: `limit`, `exchange`, `provider`, and
`include_quotes`. Use the exact returned `canonical_symbol` in a canonical
payload; do not invent provider suffixes.

U.S. stocks and ETFs normally use a bare ticker such as `AAPL` or `SPY`.
European stocks and ETFs are not currently supported for canonical live
submission.

## 8) Errors

Most errors use:

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

Common canonical failures:

- `400 INVALID_SIGNAL_ENVELOPE`: wrong field, type, timestamp, symbol, or protocol version.
- `401 AUTHENTICATION_REQUIRED`: missing or invalid credentials.
- `403 SANDBOX_SCOPE_RESTRICTION`: sandbox key used with a normal strategy.
- `404 STRATEGY_NOT_FOUND`: strategy ID does not match an owned strategy.
- `409 STRATEGY_ARCHIVED`: target strategy is archived.
- `409 IDEMPOTENCY_PAYLOAD_CONFLICT`: key reused with different content.
- `413 SIGNAL_CAPACITY_EXCEEDED`: body exceeds 256 KiB or 500 targets.
- `422 MANAGER_NAV_UNAVAILABLE`: strategy value could not be captured.

Do not automatically retry validation, authentication, authorization, or
conflict errors. Network failures and `5xx` responses may be retried with the
identical envelope and idempotency key.

## 9) Integration checklist

1. Use one API key per integration.
2. Separate sandbox and production strategies and keys.
3. Read the strategy list and use the exact strategy `id`.
4. Resolve instruments and use the exact `canonical_symbol`.
5. Publish only to `POST /v1/signals`.
6. Generate a new idempotency key for new intent; reuse it only for an
   identical retry.
7. Store the complete acceptance receipt.
8. Poll canonical status and follow any returned `trade_status_path`.
9. Alert on repeated `4xx` responses; do not switch submission formats.

## 10) Changelog

- **1.2 (2026-09-19)**
  - Established canonical `/v1/signals` as the only documented submission path.
  - Clarified that API-key and strategy calls are current management APIs on
    the same base URL, not legacy signal calls.
  - Removed alternative submission contracts and examples.

- **1.1 (2026-09-09)**
  - Made the canonical contract self-contained for human and AI integration
    builders.
  - Added strategy ID and symbol discovery, payload constraints, receipt
    handling, retries, and canonical error codes.
