PeakBot Docs
Bots EndpointUser Trade Participations

Create Participation

Create user trade participations linking bot trades to user broker accounts

Create user trade participations that link bot trades to specific user broker accounts. Participations track which users are participating in which bot trades and their broker order IDs. The system uses an outbox pattern to asynchronously fetch order details from brokers.

Endpoint

POST /api/bots/:slug/participations

Authentication

All requests require bot API key authentication via the x-api-key header.

headers: {
  'x-api-key': 'your-bot-api-key'
}

Path Parameters

ParameterTypeRequiredDescription
slugstringYesBot identifier (alphanumeric, hyphens, underscores only)

Slug Validation

The slug must match the regex pattern: /^[a-zA-Z0-9_-]+$/

Request Body

{
  participations: Array<{
    botTradeId: string;           // Required: UUID of bot trade
    userId: string;               // Required: UUID of user
    brokerAccountId: string;      // Required: UUID of user broker account
    brokerOrderId: string;        // Required: Broker order ID
    childOrderIds?: string[];    // Optional: Array of child order IDs
    units?: number;               // Optional: Number of units (defaults to 1)
  }>;
}

Request Body Schema

FieldTypeRequiredDescription
participationsarrayYesArray of participation objects (must not be empty)
participations[].botTradeIdstring (UUID)YesUUID of the bot trade
participations[].userIdstring (UUID)YesUUID of the user
participations[].brokerAccountIdstring (UUID)YesUUID of the user broker account
participations[].brokerOrderIdstringYesBroker order ID from the broker API
participations[].childOrderIdsstring[]NoArray of child order IDs (for complex orders)
participations[].unitsnumberNoNumber of units/contracts (defaults to 1)

Validation Rules

  • participations must be a non-empty array
  • Each botTradeId must be a valid UUID string
  • Each userId must be a valid UUID string
  • Each brokerAccountId must be a valid UUID string
  • Each brokerOrderId must be a non-empty string
  • childOrderIds must be an array of strings if provided
  • Each participation is validated independently:
    • Bot trade must exist
    • Bot trade must belong to the specified bot
    • User broker account must exist for the specified user
  • Participations 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": {
        "participationId": "111e4567-e89b-12d3-a456-426614174001",
        "userId": "789e4567-e89b-12d3-a456-426614174002",
        "botTradeId": "222e4567-e89b-12d3-a456-426614174003"
      }
    },
    {
      "status": "success",
      "data": {
        "participationId": "333e4567-e89b-12d3-a456-426614174004",
        "userId": "444e4567-e89b-12d3-a456-426614174005",
        "botTradeId": "222e4567-e89b-12d3-a456-426614174003"
      }
    },
    {
      "status": "error",
      "error": "Bot trade '555e4567-e89b-12d3-a456-426614174006' does not belong to bot 'my-bot'"
    }
  ]
}

Response Schema

FieldTypeDescription
successbooleanAlways true for successful requests (even with partial failures)
summaryobjectSummary of participation creation results
summary.totalnumberTotal number of participations attempted
summary.succeedednumberNumber of successful participations
summary.failednumberNumber of failed participations
resultsarrayArray of individual participation results
results[].statusstringEither "success" or "error"
results[].dataobject(Success only) Created participation data
results[].data.participationIdstringUUID of the created participation
results[].data.userIdstringUUID of the user
results[].data.botTradeIdstringUUID of the bot trade
results[].errorstring(Error only) Error message

Notes

  • Participations are processed independently, so some may succeed while others fail
  • If a participation already exists, it is retrieved rather than creating a duplicate
  • An outbox event is created for each successful participation to asynchronously fetch order details from the broker
  • The system validates that bot trades belong to the specified bot and that broker accounts belong to the specified user

Error Responses

400 Bad Request

Invalid JSON:

{
  "error": "Invalid JSON in request body",
  "code": "BAD_REQUEST"
}

Missing participations array:

{
  "error": "participations is required and must be a non-empty array",
  "code": "BAD_REQUEST"
}

Invalid botTradeId:

{
  "error": "participations[0].botTradeId is required and must be a string",
  "code": "BAD_REQUEST"
}

Invalid userId:

{
  "error": "participations[0].userId is required and must be a string",
  "code": "BAD_REQUEST"
}

Invalid brokerAccountId:

{
  "error": "participations[0].brokerAccountId is required and must be a string",
  "code": "BAD_REQUEST"
}

Invalid childOrderIds:

{
  "error": "participations[0].childOrderIds must be an array of strings",
  "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 Participation {
  botTradeId: string;
  userId: string;
  brokerAccountId: string;
  brokerOrderId: string;
  childOrderIds?: string[];
  units?: number;
}

async function createParticipations(
  botSlug: string,
  apiKey: string,
  participations: Participation[]
) {
  const response = await fetch(
    `https://api.example.com/api/bots/${botSlug}/participations`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": apiKey,
      },
      body: JSON.stringify({ participations }),
    }
  );

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Failed to create participations: ${error.error}`);
  }

  return await response.json();
}

// Example usage - create participations for multiple users
const participations = [
  {
    botTradeId: "222e4567-e89b-12d3-a456-426614174003",
    userId: "789e4567-e89b-12d3-a456-426614174002",
    brokerAccountId: "550e8400-e29b-41d4-a716-446655440000",
    brokerOrderId: "TRADIER-ORDER-12345",
    units: 1
  },
  {
    botTradeId: "222e4567-e89b-12d3-a456-426614174003",
    userId: "444e4567-e89b-12d3-a456-426614174005",
    brokerAccountId: "550e8400-e29b-41d4-a716-446655440001",
    brokerOrderId: "TRADIER-ORDER-12346",
    childOrderIds: ["TRADIER-ORDER-12347", "TRADIER-ORDER-12348"],
    units: 2
  }
];

const result = await createParticipations("my-bot", "your-api-key", participations);

console.log(`Created ${result.summary.succeeded} of ${result.summary.total} participations`);

// Check for failures
const failures = result.results.filter(r => r.status === "error");
if (failures.length > 0) {
  console.error("Some participations failed:", failures);
}

Python

from typing import List, Dict, Any, Optional
import requests

def create_participations(
    bot_slug: str,
    api_key: str,
    participations: List[Dict[str, Any]]
) -> Dict[str, Any]:
    """
    Create user trade participations.
    
    Args:
        bot_slug: Bot identifier
        api_key: Bot API key
        participations: List of participation dictionaries, each containing:
            - botTradeId: UUID of bot trade
            - userId: UUID of user
            - brokerAccountId: UUID of user broker account
            - brokerOrderId: Broker order ID
            - childOrderIds: Optional list of child order IDs
            - units: Optional number of units (defaults to 1)
            
    Returns:
        Response dictionary with participation results
        
    Raises:
        requests.HTTPError: If the request fails
    """
    url = f"https://api.example.com/api/bots/{bot_slug}/participations"
    
    payload = {
        "participations": participations
    }
    
    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 - create participations for multiple users
participations = [
    {
        "botTradeId": "222e4567-e89b-12d3-a456-426614174003",
        "userId": "789e4567-e89b-12d3-a456-426614174002",
        "brokerAccountId": "550e8400-e29b-41d4-a716-446655440000",
        "brokerOrderId": "TRADIER-ORDER-12345",
        "units": 1
    },
    {
        "botTradeId": "222e4567-e89b-12d3-a456-426614174003",
        "userId": "444e4567-e89b-12d3-a456-426614174005",
        "brokerAccountId": "550e8400-e29b-41d4-a716-446655440001",
        "brokerOrderId": "TRADIER-ORDER-12346",
        "childOrderIds": ["TRADIER-ORDER-12347", "TRADIER-ORDER-12348"],
        "units": 2
    }
]

result = create_participations("my-bot", "your-api-key", participations)

print(f"Created {result['summary']['succeeded']} of {result['summary']['total']} participations")

# Check for failures
failures = [r for r in result["results"] if r["status"] == "error"]
if failures:
    print("Some participations failed:", failures)

Use Cases

// After executing a trade, link it to user accounts
const participations = await createParticipations("my-bot", apiKey, [
  {
    botTradeId: tradeId,
    userId: userId,
    brokerAccountId: accountId,
    brokerOrderId: brokerOrderId,
    units: 1
  }
]);

Complex Order with Child Orders

# Link trade with bracket order (parent + child orders)
participations = create_participations(
    bot_slug="my-bot",
    api_key=api_key,
    participations=[{
        "botTradeId": trade_id,
        "userId": user_id,
        "brokerAccountId": account_id,
        "brokerOrderId": parent_order_id,
        "childOrderIds": [stop_loss_order_id, take_profit_order_id],
        "units": 1
    }]
)

Bulk Participation Creation

// Create participations for multiple users in the same trade
const userAccounts = [
  { userId: "user1", accountId: "account1", orderId: "order1" },
  { userId: "user2", accountId: "account2", orderId: "order2" },
  { userId: "user3", accountId: "account3", orderId: "order3" }
];

const participations = userAccounts.map(ua => ({
  botTradeId: tradeId,
  userId: ua.userId,
  brokerAccountId: ua.accountId,
  brokerOrderId: ua.orderId,
  units: 1
}));

const result = await createParticipations("my-bot", apiKey, participations);
console.log(`Successfully linked ${result.summary.succeeded} users to trade`);