Skip to main content

Create Signal

Submit trading signals to UTM for execution, scheduling, or manual review.

POST /api/v1/signals

Authentication

Supports both authentication methods:

  • JWT Bearer Token: For web UI and session-based access
  • API Key: For webhooks and automation (requires signals:write scope)
# Using JWT
curl -X POST https://api.universaltrademanager.com/api/v1/signals \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"symbol": "AAPL", "action": "openLong", "executeMode": "pending", ...}'

# Using API Key
curl -X POST https://api.universaltrademanager.com/api/v1/signals \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"symbol": "AAPL", "action": "openLong", "executeMode": "immediate", ...}'

Execute Mode

The executeMode field determines how the signal is processed:

ModeDescription
immediateProcess signal and place order immediately
scheduledSchedule for processing at scheduledProcessAt
pendingCreate in pending status for manual review/approval

Request Body

Required Fields

FieldTypeDescription
symbolstringTrading symbol (1-20 chars, auto-uppercased)
actionenumSignal action: openLong, closeLong, openShort, closeShort
accountIdUUIDTarget trading account ID
quantitynumberOrder quantity (must be positive)
executeModeenumHow to process: immediate, scheduled, pending

Optional Fields

FieldTypeDefaultDescription
scheduledProcessAtISO datetime-When to process (required for scheduled mode)
timezonestring-IANA timezone (required when scheduledProcessAt is set)
strategyIdUUID-Associated strategy ID
quantityTypeenum"fixed"fixed, percentEquity, dollarAmount
orderTypeenum"market"market, limit, stop, stopLimit
limitPricenumber-Required for limit and stopLimit orders
stopPricenumber-Required for stop and stopLimit orders
timeInForceenum"day"day, gtc, opg, cls, ioc, fok
extendedHoursbooleanfalseAllow pre/post-market trading
validUntilISO datetime-Signal expiration (requires timezone)
sourceIdstring-Idempotency key (max 255 chars, scoped per account)
replaceExistingbooleanfalseReplace existing signal with same sourceId
metadataobject-Custom key-value pairs

Timed Exit Fields

Automatically create an exit order after entry fills:

FieldTypeDescription
exitTriggerTypeenumExit trigger: timed
exitTriggerValueintegerMinutes after entry to exit
exitOrderTypeenumExit order type: market, limit
exitLimitPricenumberLimit price for exit (if limit order)

Examples

Immediate Market Order

Execute a market order immediately:

{
"symbol": "AAPL",
"action": "openLong",
"accountId": "550e8400-e29b-41d4-a716-446655440000",
"quantity": 100,
"executeMode": "immediate"
}

Immediate with Timed Exit

Open position and close after 30 minutes:

{
"symbol": "AAPL",
"action": "openLong",
"accountId": "550e8400-e29b-41d4-a716-446655440000",
"quantity": 100,
"executeMode": "immediate",
"exitTriggerType": "timed",
"exitTriggerValue": 30,
"exitOrderType": "market"
}

Scheduled Signal

Schedule signal for processing at a specific time:

{
"symbol": "MSFT",
"action": "openLong",
"accountId": "550e8400-e29b-41d4-a716-446655440000",
"quantity": 75,
"executeMode": "scheduled",
"scheduledProcessAt": "2026-02-07T09:25:00",
"timezone": "America/New_York",
"sourceId": "my-strategy-signal-001"
}

Pending Signal for Review

Create signal for manual review before execution:

{
"symbol": "TSLA",
"action": "openLong",
"accountId": "550e8400-e29b-41d4-a716-446655440000",
"quantity": 50,
"executeMode": "pending",
"orderType": "limit",
"limitPrice": 250.0
}

Limit Order with Replace

Replace existing signal with same sourceId:

{
"symbol": "GOOGL",
"action": "openShort",
"accountId": "550e8400-e29b-41d4-a716-446655440000",
"quantity": 25,
"executeMode": "immediate",
"orderType": "stopLimit",
"stopPrice": 145.0,
"limitPrice": 144.0,
"sourceId": "my-ref-456",
"replaceExisting": true
}

Dollar Amount Position Sizing

{
"symbol": "SPY",
"action": "openLong",
"accountId": "550e8400-e29b-41d4-a716-446655440000",
"quantity": 5000,
"quantityType": "dollarAmount",
"executeMode": "immediate"
}

Response

Success - Immediate Execution (201)

{
"success": true,
"message": "Signal processed successfully",
"signal": {
"id": "550e8400-e29b-41d4-a716-446655440099",
"status": "executed",
"orderId": "550e8400-e29b-41d4-a716-446655440098",
"executeMode": "immediate"
}
}

Success - Scheduled (202)

{
"success": true,
"message": "Signal scheduled for processing",
"signal": {
"id": "550e8400-e29b-41d4-a716-446655440099",
"status": "scheduled",
"scheduledAt": "2026-02-07T14:25:00.000Z",
"executeMode": "scheduled"
}
}

Success - Pending (201)

{
"success": true,
"message": "Signal created",
"signal": {
"id": "550e8400-e29b-41d4-a716-446655440099",
"status": "pending",
"executeMode": "pending"
}
}

Validation Error (400)

{
"success": false,
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"details": [
{
"code": "custom",
"message": "scheduledProcessAt is required for scheduled execute mode",
"path": ["scheduledProcessAt"]
}
]
}

Duplicate Signal (200)

When sourceId matches an existing non-rejected signal:

{
"success": true,
"message": "Signal with sourceId 'my-ref' already exists with status 'pending'. Use replaceExisting=true to supersede.",
"signal": {
"id": "550e8400-e29b-41d4-a716-446655440099",
"status": "pending",
"executeMode": "pending"
}
}

Response Codes

CodeDescription
200Existing signal returned (idempotent)
201Signal created/executed
202Signal scheduled for future processing
400Validation error or signal rejected
401Missing or invalid authentication
404Account or strategy not found
503Service disabled

Idempotency

Use sourceId to prevent duplicate signals:

  • If a signal with the same sourceId exists (non-rejected), the existing signal is returned
  • With replaceExisting: true, the existing signal is cancelled and a new one created
  • Same sourceId can exist on different accounts (scoped per account)
  • Only rejected signals allow retry with the same sourceId

Processing Pending Signals

Signals created with executeMode: "pending" can be processed manually:

POST /api/v1/signals/:id/process

This endpoint requires JWT authentication and converts the pending signal into an order.