Bots EndpointBot Data
Update User Bot State
Update user bot state for multiple account-ticker combinations in bulk
Update user bot state for one or more account-ticker combinations. This endpoint allows bots to persist their internal state (e.g., position tracking, strategy state, indicators) for each user account and ticker combination. State updates are processed independently, so partial failures won't affect successful updates.
Endpoint
POST /api/bots/:slug/stateAuthentication
All requests require bot API key authentication via the x-api-key header.
headers: {
'x-api-key': 'your-bot-api-key'
}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Bot identifier (alphanumeric, hyphens, underscores only) |
Slug Validation
The slug must match the regex pattern: /^[a-zA-Z0-9_-]+$/
Request Body
{
states: Array<{
accountId: string; // UUID of user broker account
tickerId: string; // UUID of ticker
state: Record<string, unknown>; // Bot-specific state object
}>;
}Request Body Schema
| Field | Type | Required | Description |
|---|---|---|---|
states | array | Yes | Array of state update objects |
states[].accountId | string (UUID) | Yes | UUID of the user broker account |
states[].tickerId | string (UUID) | Yes | UUID of the ticker |
states[].state | object | Yes | Bot-specific state object (must be a plain object, not array) |
Validation Rules
statesmust be a non-empty array- Each
accountIdmust be a valid UUID string - Each
tickerIdmust be a valid UUID string - Each
statemust be a plain object (not null, not array, not primitive) - All state updates are processed independently using
Promise.allSettled, so partial failures are handled gracefully
Response
Success Response (200 OK)
{
"success": true,
"summary": {
"total": 3,
"succeeded": 2,
"failed": 1
},
"results": [
{
"status": "success",
"data": {
"id": "111e4567-e89b-12d3-a456-426614174001",
"botId": "123e4567-e89b-12d3-a456-426614174000",
"userBrokerAccountId": "550e8400-e29b-41d4-a716-446655440000",
"tickerId": "222e4567-e89b-12d3-a456-426614174002",
"state": {
"lastPrice": 150.25,
"position": "long"
}
},
"accountId": "550e8400-e29b-41d4-a716-446655440000",
"tickerId": "222e4567-e89b-12d3-a456-426614174002"
},
{
"status": "success",
"data": {
"id": "333e4567-e89b-12d3-a456-426614174003",
"botId": "123e4567-e89b-12d3-a456-426614174000",
"userBrokerAccountId": "550e8400-e29b-41d4-a716-446655440001",
"tickerId": "444e4567-e89b-12d3-a456-426614174004",
"state": {
"indicators": {
"rsi": 65.5,
"macd": 1.2
}
}
},
"accountId": "550e8400-e29b-41d4-a716-446655440001",
"tickerId": "444e4567-e89b-12d3-a456-426614174004"
},
{
"status": "error",
"error": "Ticker not found",
"accountId": "550e8400-e29b-41d4-a716-446655440002",
"tickerId": "555e4567-e89b-12d3-a456-426614174005",
"index": 2
}
]
}Response Schema
| Field | Type | Description |
|---|---|---|
success | boolean | Always true for successful requests (even with partial failures) |
summary | object | Summary of update results |
summary.total | number | Total number of state updates attempted |
summary.succeeded | number | Number of successful updates |
summary.failed | number | Number of failed updates |
results | array | Array of individual update results |
results[].status | string | Either "success" or "error" |
results[].data | object | (Success only) Updated user bot state object |
results[].error | string | (Error only) Error message |
results[].accountId | string | UUID of the account for this update |
results[].tickerId | string | UUID of the ticker for this update |
results[].index | number | (Error only) Index in the original request array |
Notes
- Updates are processed independently, so some may succeed while others fail
- Successful updates are upserted (created if not exists, updated if exists)
- The response includes both successful and failed updates for transparency
- Failed updates are logged server-side but don't cause the entire request to fail
Error Responses
400 Bad Request
Invalid JSON:
{
"error": "Invalid JSON in request body",
"code": "BAD_REQUEST"
}Missing states array:
{
"error": "states must be an array",
"code": "BAD_REQUEST"
}Empty states array:
{
"error": "states array cannot be empty",
"code": "BAD_REQUEST"
}Invalid accountId:
{
"error": "states[0].accountId is required and must be a string",
"code": "BAD_REQUEST"
}Invalid tickerId:
{
"error": "states[0].tickerId is required and must be a string",
"code": "BAD_REQUEST"
}Invalid state object:
{
"error": "states[0].state is required and must be an object",
"code": "BAD_REQUEST"
}Invalid slug format:
{
"error": "Invalid slug format. Only alphanumeric characters, hyphens, and underscores are allowed.",
"code": "BAD_REQUEST"
}401 Unauthorized
Missing API key:
{
"message": "API key required. Include x-api-key header."
}403 Forbidden
Invalid API key:
{
"message": "Invalid API key for this bot"
}404 Not Found
Bot not found:
{
"error": "Bot with slug 'my-bot' not found",
"code": "NOT_FOUND"
}500 Internal Server Error
{
"error": "Internal server error",
"code": "INTERNAL_SERVER_ERROR"
}Code Examples
TypeScript
interface StateUpdate {
accountId: string;
tickerId: string;
state: Record<string, unknown>;
}
async function updateBotState(
botSlug: string,
apiKey: string,
states: StateUpdate[]
) {
const response = await fetch(`https://api.example.com/api/bots/${botSlug}/state`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": apiKey,
},
body: JSON.stringify({ states }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Failed to update user bot state: ${error.error}`);
}
return await response.json();
}
// Example usage - update state for multiple account-ticker combinations
const states = [
{
accountId: "550e8400-e29b-41d4-a716-446655440000",
tickerId: "222e4567-e89b-12d3-a456-426614174002",
state: {
lastPrice: 150.25,
position: "long",
entryPrice: 145.00,
stopLoss: 140.00
}
},
{
accountId: "550e8400-e29b-41d4-a716-446655440001",
tickerId: "444e4567-e89b-12d3-a456-426614174004",
state: {
indicators: {
rsi: 65.5,
macd: 1.2,
ema20: 148.50
},
signals: ["buy"]
}
}
];
const result = await updateBotState("my-trading-bot", "your-api-key", states);
console.log(`Updated ${result.summary.succeeded} of ${result.summary.total} states`);
// Check for failures
const failures = result.results.filter(r => r.status === "error");
if (failures.length > 0) {
console.error("Some updates failed:", failures);
}Python
from typing import List, Dict, Any
import requests
def update_bot_state(
bot_slug: str,
api_key: str,
states: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""
Update user bot state for multiple account-ticker combinations.
Args:
bot_slug: Bot identifier
api_key: Bot API key
states: List of state update dictionaries, each containing:
- accountId: UUID of user broker account
- tickerId: UUID of ticker
- state: Dictionary of bot-specific state data
Returns:
Response dictionary with update results
Raises:
requests.HTTPError: If the request fails
"""
url = f"https://api.example.com/api/bots/{bot_slug}/state"
payload = {
"states": states
}
headers = {
"Content-Type": "application/json",
"x-api-key": api_key
}
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
return response.json()
# Example usage - update state for multiple account-ticker combinations
states = [
{
"accountId": "550e8400-e29b-41d4-a716-446655440000",
"tickerId": "222e4567-e89b-12d3-a456-426614174002",
"state": {
"lastPrice": 150.25,
"position": "long",
"entryPrice": 145.00,
"stopLoss": 140.00
}
},
{
"accountId": "550e8400-e29b-41d4-a716-446655440001",
"tickerId": "444e4567-e89b-12d3-a456-426614174004",
"state": {
"indicators": {
"rsi": 65.5,
"macd": 1.2,
"ema20": 148.50
},
"signals": ["buy"]
}
}
]
result = update_bot_state("my-trading-bot", "your-api-key", states)
print(f"Updated {result['summary']['succeeded']} of {result['summary']['total']} states")
# Check for failures
failures = [r for r in result["results"] if r["status"] == "error"]
if failures:
print("Some updates failed:", failures)Use Cases
Update Position State
// Update state after opening a position
await updateBotState("my-bot", apiKey, [{
accountId: accountId,
tickerId: tickerId,
state: {
position: "long",
entryPrice: 150.25,
quantity: 100,
stopLoss: 145.00,
takeProfit: 160.00,
openedAt: new Date().toISOString()
}
}]);Bulk State Update After Market Close
# Update state for all active positions at end of day
states = []
for account_id, ticker_id, position_data in active_positions:
states.append({
"accountId": account_id,
"tickerId": ticker_id,
"state": {
"endOfDayPrice": position_data["current_price"],
"unrealizedPnl": position_data["unrealized_pnl"],
"lastUpdate": datetime.now().isoformat()
}
})
result = update_bot_state("my-bot", api_key, states)
print(f"Updated {result['summary']['succeeded']} positions")Update Indicator State
// Update technical indicator state
await updateBotState("my-bot", apiKey, [{
accountId: accountId,
tickerId: tickerId,
state: {
indicators: {
rsi: calculateRSI(prices),
macd: calculateMACD(prices),
bollingerBands: calculateBollingerBands(prices)
},
lastCalculation: new Date().toISOString()
}
}]);