Polyhub-skills polyhub_copy

Manage copy-trading tasks, view signals, positions and trades on Polyhub using an API key.

install
source · Clone the upstream repo
git clone https://github.com/HubbleVision/polyhub-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/HubbleVision/polyhub-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/openclaw/skills/polyhub_copy" ~/.claude/skills/hubblevision-polyhub-skills-polyhub-copy-8e49ed && rm -rf "$T"
OpenClaw · Install into ~/.openclaw/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/HubbleVision/polyhub-skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/openclaw/skills/polyhub_copy" ~/.openclaw/skills/hubblevision-polyhub-skills-polyhub-copy-8e49ed && rm -rf "$T"
manifest: openclaw/skills/polyhub_copy/SKILL.md
source content

Polyhub Copy Skill

Version: v0.3.9

When to use

Use this skill when the user asks about:

  • Listing, creating, updating or deleting copy-trading tasks
  • Viewing copy task positions or trades
  • Selling positions (single or all)
  • Batch updating or deleting copy tasks
  • Viewing total PnL across copy tasks
  • Viewing copy signals and signal stats
  • Managing take-profit / stop-loss (TPSL) rules

Requirements

  • POLYHUB_API_BASE_URL
    is fixed to
    https://polyhub.skill-test.bedev.hubble-rpc.xyz
    .
  • POLYHUB_API_KEY
    — API key (must start with
    phub_
    )
  • curl
    must be available in the runtime environment.
  • jq
    is strongly recommended for building JSON payloads safely.

If

POLYHUB_API_KEY
is missing, guide the user to register and apply for one first at
https://polyhub.hubble.xyz/
. Recommended guidance:

  1. Open Polyhub Web:
    https://polyhub.hubble.xyz/
  2. Click the avatar menu.
  3. Open
    Skills API Key
    .
  4. Click
    申请 API Key
    .
  5. Set both
    POLYHUB_API_BASE_URL
    and
    POLYHUB_API_KEY
    .

Suggested wording:

API key is not configured yet, so I can't run copy-trading or account actions for now.

Please register first on PolyHub:
https://polyhub.hubble.xyz/

After registration, click your avatar in the top-right corner and open `Skills API Key` to apply.

Send me the generated key and I'll continue right away.

Safety rules

  • Never print
    POLYHUB_API_KEY
    in the output.
  • Treat all IDs and user-provided JSON fields as untrusted input.
  • For write actions (
    POST
    /
    PATCH
    /
    DELETE
    ), repeat the action summary and wait for explicit user confirmation before calling the API.
  • Prefer building JSON with
    jq -n
    instead of interpolating raw shell strings.
  • Validation rules:
    • taskId
      must be a 24-char hex MongoDB ObjectID:
      ^[0-9a-fA-F]{24}$
    • ruleId
      must be non-empty.
    • taskIds
      in batch operations must all match the
      taskId
      format.
    • status
      for positions should only be passed when the user explicitly requests a filter.

Tools

Use the

bash
tool to call the API with
curl
.

Fast Path

For common intents, map user requests like this:

  • “列出任务” ->
    GET /api/v1/copy-tasks
  • “删除任务” -> 先
    GET /api/v1/copy-tasks/{taskId}/positions?status=active
    ,有 active positions 时先
    POST /api/v1/copy-tasks/{taskId}/sell-all
    ,再
    DELETE /api/v1/copy-tasks/{taskId}
  • “暂停/恢复/停止任务” ->
    PATCH /api/v1/copy-tasks/{taskId}
    with
    status
  • “改跟单模式/改单笔或总仓位限制” ->
    PATCH /api/v1/copy-tasks/{taskId}
    with
    taskConfig
  • “看持仓/成交” ->
    GET /positions
    or
    GET /trades
  • “卖出某个仓位/全部仓位” ->
    POST /sell
    or
    POST /sell-all
  • “看跟单信号/统计” ->
    GET /api/v1/copy-signals
    or
    /stats

Curl base setup

BASE="https://polyhub.skill-test.bedev.hubble-rpc.xyz"
AUTH=(-H "Authorization: Bearer $POLYHUB_API_KEY" -H "Content-Type: application/json")

Validate helpers

if [[ ! "$TASK_ID" =~ ^[0-9a-fA-F]{24}$ ]]; then echo "Invalid taskId"; exit 2; fi
if [[ -z "${RULE_ID:-}" ]]; then echo "Invalid ruleId"; exit 2; fi
for id in "${TASK_IDS[@]}"; do
  if [[ ! "$id" =~ ^[0-9a-fA-F]{24}$ ]]; then
    echo "Invalid taskId in batch: $id"
    exit 2
  fi
done

Copy Tasks

Action: List copy tasks

  • GET /api/v1/copy-tasks
  • Query params:
    includeDeleted=true|false
    (default: true)
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks?includeDeleted=true"

Guidance:

  • Use
    includeDeleted=true
    when the user wants a full history or is looking for a previously deleted task.
  • Use
    includeDeleted=false
    when the user only wants active/non-deleted tasks.

Action: Create copy task

  • POST /api/v1/copy-tasks
  • Required field:
    targetTrader
    (Polymarket address of the smart money)
  • Optional fields from
    domain.CreateCopyTaskPayload
    :
    targetUsername
    ,
    taskConfig
    ,
    filteredByTag
    ,
    tpslRules

Before calling: ask the user for

targetTrader
at minimum. Summarize and confirm.

Minimum fields to ask:

  • Always ask:
    targetTrader
  • Ask only if needed:
    filteredByTag
    ,
    targetUsername
  • If the user wants custom copy behavior: ask for the relevant
    taskConfig
    fields explicitly
  • If the user wants TPSL on creation: ask for
    tpslRules
PAYLOAD="$(jq -n \
  --arg targetTrader "0x..." \
  '{targetTrader: $targetTrader}')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks" \
  -d "$PAYLOAD"

If the user wants advanced config, build

taskConfig
and
tpslRules
explicitly instead of accepting arbitrary JSON.

Pre-flight: Balance Check

Before creating a copy task, check if the user has sufficient balance:

curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/portfolio/stats"

Check

availableBalance
from the response:

  • If
    availableBalance >= 100
    : proceed with task creation
  • If
    availableBalance < 100
    : show deposit guidance (see below)
  • If
    availableBalance < 10
    : strongly recommend depositing before creating any tasks

Deposit Guidance

Depositing is not supported via skill or API. Always direct the user to the web UI.

When balance is insufficient, present this message:

当前可用余额: $XX

建议至少 $100 才能有效跟单。充值只能通过网页端操作:
👉 https://polyhub.hubble.xyz/copy-history?action=deposit
(打开后会自动弹出充值窗口)
充值完成后回来告诉我,我帮你继续创建跟单任务。

Quick Copy from Discover

When the user comes from a discover flow (already has a target address and optionally a tag), use this streamlined creation:

  1. Run balance check (see Pre-flight above)
  2. Confirm with user: "要跟单 {address} 吗?标签: {tag or '全部市场'}"
  3. Create task:
PAYLOAD="$(jq -n \
  --arg targetTrader "0x..." \
  --arg filteredByTag "Sports" \
  '{targetTrader: $targetTrader, filteredByTag: $filteredByTag}')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks" \
  -d "$PAYLOAD"

If

filteredByTag
is not specified (user wants all markets), omit it:

PAYLOAD="$(jq -n \
  --arg targetTrader "0x..." \
  '{targetTrader: $targetTrader}')"

After successful creation, show:

✅ 跟单任务已创建
📊 查看任务: https://polyhub.hubble.xyz/copy-history

Action: Get copy task

  • GET /api/v1/copy-tasks/{taskId}
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID"

Action: Update copy task

  • PATCH /api/v1/copy-tasks/{taskId}
  • Body: partial JSON. Common fields:
    • status
    • taskConfig
    • filteredByTag
    • targetUsername
    • tpslRules
      using
      create
      /
      update
      /
      cancel

Before calling: validate

taskId
, summarize changes, and confirm.

Minimum fields to ask:

  • Always ask:
    taskId
  • Ask only the fields the user wants to change:
    • Task lifecycle:
      status
    • Copy behavior:
      taskConfig
    • Trader label/tag filters:
      targetUsername
      ,
      filteredByTag
    • TPSL maintenance:
      tpslRules
PAYLOAD="$(jq -n \
  --arg status "PAUSED" \
  '{status: $status}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Example: update

taskConfig
safely

PAYLOAD="$(jq -n \
  --argjson buyOnly true \
  --argjson maxBoughtPerMarket 50 \
  '{taskConfig: {buyOnly: $buyOnly, maxBoughtPerMarket: $maxBoughtPerMarket}}')"

Example: change copy mode to

ONE_TO_ONE

PAYLOAD="$(jq -n \
  '{taskConfig: {copyMode: "ONE_TO_ONE"}}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Example: change copy mode to

FIXED_SIZE

FIXED_SIZE
should include
taskConfig.fixedAmount
.

PAYLOAD="$(jq -n \
  --argjson fixedAmount 5 \
  '{taskConfig: {copyMode: "FIXED_SIZE", fixedAmount: $fixedAmount}}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Copy mode guidance:

  • taskConfig.copyMode
    is the field that controls the task's copy mode.
  • Confirmed mode values in the current codebase:
    ONE_TO_ONE
    ,
    FIXED_SIZE
    .
  • If switching to
    FIXED_SIZE
    , also ask for
    taskConfig.fixedAmount
    .
  • If switching to
    ONE_TO_ONE
    , omit
    fixedAmount
    unless the user explicitly wants to keep it stored.

TaskConfig field guidance

Use these fields when the user wants to change task-level copy behavior. Prefer changing only the fields the user explicitly asked for.

Common fields:

  • taskConfig.copyMode
    • Controls copy sizing mode.
    • Confirmed values:
      ONE_TO_ONE
      ,
      FIXED_SIZE
      .
  • taskConfig.fixedAmount
    • Used when
      copyMode=FIXED_SIZE
      .
    • Represents a fixed notional amount for copied orders.
  • taskConfig.navMultiplier
    • Multiplies the computed order size.
    • Current default in backend user config is
      1
      .
  • taskConfig.positionUtilization
    • Position utilization / max amount per copy-trade style control.
    • Backend default is
      0
      , which means no filter.
  • taskConfig.buyOnly
    • If
      true
      , non-
      BUY
      signals are skipped.
  • taskConfig.buyLimitPerTradeMin
    • Minimum allowed copied buy amount per trade.
    • 0
      effectively means no lower bound.
  • taskConfig.buyLimitPerTradeMax
    • Maximum allowed copied buy amount per trade.
    • 0
      effectively means no upper bound.
  • taskConfig.maxBoughtPerCopyTask
    • Caps total bought amount at task level.
    • 0
      effectively means no cap.
  • taskConfig.maxBoughtPerMarket
    • Caps total bought amount for a single market inside the task.
    • 0
      effectively means no cap.
  • taskConfig.priceRangeMin
    • Minimum allowed execution price.
    • Default from user config is typically
      0.01
      .
  • taskConfig.priceRangeMax
    • Maximum allowed execution price.
    • Default from user config is typically
      0.99
      .
  • taskConfig.minLiquidity
    • Minimum market liquidity filter.
    • 0
      effectively means no filter.
  • taskConfig.minFillSize
    • Minimum fill shares filter.
    • 0
      effectively means no filter.
  • taskConfig.maxPriceDeviation
    • Maximum allowed price deviation percentage.
  • taskConfig.expiryHours
    • Order expiry configuration for copied orders.
    • Backend default from user config is typically
      0
      .
  • taskConfig.convictionLevel
    • Conviction-based filter level.
    • Confirmed values:
      OFF
      ,
      LOOSE
      ,
      STANDARD
      ,
      STRICT
      .
    • Backend default is
      STANDARD
      .

Recommended clarification rules before updating:

  • If the user says “改成固定金额跟单”, ask for
    fixedAmount
    .
  • If the user says “放大/缩小仓位”, clarify whether they mean
    navMultiplier
    or
    fixedAmount
    .
  • If the user says “限制单笔金额”, clarify whether they mean
    buyLimitPerTradeMin
    ,
    buyLimitPerTradeMax
    , or both.
  • If the user says “限制总仓位”, clarify whether they mean
    maxBoughtPerCopyTask
    or
    maxBoughtPerMarket
    .
  • If the user says “限制价格区间”, ask for both
    priceRangeMin
    and
    priceRangeMax
    .
  • If the user says “调严格一点/调宽松一点”, clarify whether they mean
    convictionLevel
    .
  • If the user says “恢复跟单/暂停跟单/停止任务”, clarify whether they mean task
    status
    or a
    taskConfig
    change.

Example: update

navMultiplier

PAYLOAD="$(jq -n \
  --argjson navMultiplier 1.5 \
  '{taskConfig: {navMultiplier: $navMultiplier}}')"

Example: enable buy-only mode

PAYLOAD="$(jq -n \
  --argjson buyOnly true \
  '{taskConfig: {buyOnly: $buyOnly}}')"

Example: limit per-trade buy amount

PAYLOAD="$(jq -n \
  --argjson min 2 \
  --argjson max 10 \
  '{taskConfig: {buyLimitPerTradeMin: $min, buyLimitPerTradeMax: $max}}')"

Example: limit total bought amount

PAYLOAD="$(jq -n \
  --argjson maxBoughtPerCopyTask 100 \
  --argjson maxBoughtPerMarket 25 \
  '{taskConfig: {maxBoughtPerCopyTask: $maxBoughtPerCopyTask, maxBoughtPerMarket: $maxBoughtPerMarket}}')"

Example: restrict execution price range

PAYLOAD="$(jq -n \
  --argjson min 0.1 \
  --argjson max 0.9 \
  '{taskConfig: {priceRangeMin: $min, priceRangeMax: $max}}')"

Example: combine multiple taskConfig changes

PAYLOAD="$(jq -n \
  --argjson buyOnly true \
  --argjson navMultiplier 1.2 \
  --argjson maxBoughtPerMarket 20 \
  '{
    taskConfig: {
      buyOnly: $buyOnly,
      navMultiplier: $navMultiplier,
      maxBoughtPerMarket: $maxBoughtPerMarket
    }
  }')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Example: update

convictionLevel

PAYLOAD="$(jq -n \
  --arg convictionLevel "STRICT" \
  '{taskConfig: {convictionLevel: $convictionLevel}}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Conviction level guidance:

  • OFF
    : disable conviction filter.
  • LOOSE
    : lower threshold, more signals can pass.
  • STANDARD
    : default mode.
  • STRICT
    : higher threshold, fewer signals can pass.
  • If the user gives an unknown value, normalize to one of the four explicit options before calling the API.

Example: update

taskConfig
and
status
together

PAYLOAD="$(jq -n \
  --arg status "RUNNING" \
  --argjson buyOnly true \
  '{
    status: $status,
    taskConfig: {
      buyOnly: $buyOnly
    }
  }')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Example: update TPSL rules safely

PAYLOAD="$(jq -n \
  --arg ruleId "$RULE_ID" \
  --argjson takeProfitPct 20 \
  '{tpslRules: {update: [{id: $ruleId, takeProfitPct: $takeProfitPct}]}}')"

Example: update task status

PAYLOAD="$(jq -n \
  --arg status "PAUSED" \
  '{status: $status}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Status guidance:

  • Valid task
    status
    values for update are:
    RUNNING
    ,
    PAUSED
    ,
    STOPPED
    .
  • Use
    PAUSED
    when the user wants to temporarily stop following new trades.
  • Use
    RUNNING
    when the user wants to resume an active task.
  • Use
    STOPPED
    only when the user explicitly wants the task stopped rather than just paused.
  • Do not use
    DELETED
    in update payloads; deletion should use
    DELETE /api/v1/copy-tasks/{taskId}
    .
  • Prefer uppercase status values in payloads.

Action: Delete copy task

  • DELETE /api/v1/copy-tasks/{taskId}

Required flow before delete:

  1. Validate
    taskId
    .
  2. Check active positions with
    GET /api/v1/copy-tasks/{taskId}/positions?status=active
    .
  3. If any active positions exist, restate that deleting the task will first clear those positions, then delete the task.
  4. After explicit confirmation, call
    POST /api/v1/copy-tasks/{taskId}/sell-all
    .
  5. Only after the position cleanup step finishes, call
    DELETE /api/v1/copy-tasks/{taskId}
    .

Before calling: validate

taskId
and confirm the full sequence.

Minimum fields to ask:

  • Always ask:
    taskId

Pre-check active positions:

curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID/positions?status=active"

If active positions exist, clear them first:

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/$TASK_ID/sell-all"

Then delete the task:

curl -sS --fail-with-body "${AUTH[@]}" -X DELETE "$BASE/api/v1/copy-tasks/$TASK_ID"

Guidance:

  • There is no dedicated API to delete positions directly; use
    sell-all
    as the required position cleanup step.
  • If no active positions are returned, delete the task directly after confirmation.
  • If
    sell-all
    partially skips positions, report the skipped reasons, but continue with task deletion unless the user explicitly wants to stop.

Action: Get total PnL

  • GET /api/v1/copy-tasks/total-pnl
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/total-pnl"

Positions & Trades

Action: List positions

  • GET /api/v1/copy-tasks/{taskId}/positions
  • Query params:
    status
    (optional)
    • Confirmed values in current code:
      active
      ,
      closed
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID/positions?status=active"

Guidance:

  • active
    returns positions with remaining amount above the close threshold.
  • closed
    returns positions whose remaining amount is effectively zero.
  • If
    status
    is omitted or unknown, the backend currently returns all positions.
  • Prefer
    active
    /
    closed
    rather than
    open
    /
    closed
    .

Action: List trades

  • GET /api/v1/copy-tasks/{taskId}/trades
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID/trades"

Action: Sell position

  • POST /api/v1/copy-tasks/{taskId}/sell
  • Required field:
    marketId
    (preferred) or
    conditionId
  • Optional field:
    amount
    (partial sell; omit to sell full position)

Before calling: validate

taskId
, summarize (which market, amount), and confirm.

Minimum fields to ask:

  • Always ask:
    taskId
  • Always ask one of:
    marketId
    or
    conditionId
  • Ask
    amount
    only when the user wants a partial sell
PAYLOAD="$(jq -n \
  --arg marketId "..." \
  --argjson amount 10 \
  '{marketId: $marketId, amount: $amount}')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/$TASK_ID/sell" \
  -d "$PAYLOAD"

Action: Sell all positions

  • POST /api/v1/copy-tasks/{taskId}/sell-all

Before calling: validate

taskId
and confirm — this sells ALL positions for the task.

Minimum fields to ask:

  • Always ask:
    taskId
curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/$TASK_ID/sell-all"

Batch Operations

Action: Batch update tasks

  • POST /api/v1/copy-tasks/batch-update
  • Required:
    taskIds
    (array of task IDs)
  • Optional:
    status
    (string),
    taskConfig
    (object)

Before calling: summarize which tasks and what changes, and confirm.

Minimum fields to ask:

  • Always ask:
    taskIds
  • Ask at least one of:
    status
    ,
    taskConfig
TASK_IDS=("64f0c7e7b8e4f8c1a1b2c3d4" "64f0c7e7b8e4f8c1a1b2c3d5")
PAYLOAD="$(jq -n \
  --arg status "PAUSED" \
  --argjson taskIds "$(printf '%s\n' "${TASK_IDS[@]}" | jq -R . | jq -s .)" \
  '{taskIds: $taskIds, status: $status}')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/batch-update" \
  -d "$PAYLOAD"

Example: batch update

taskConfig.copyMode

TASK_IDS=("64f0c7e7b8e4f8c1a1b2c3d4" "64f0c7e7b8e4f8c1a1b2c3d5")
PAYLOAD="$(jq -n \
  --argjson taskIds "$(printf '%s\n' "${TASK_IDS[@]}" | jq -R . | jq -s .)" \
  '{
    taskIds: $taskIds,
    taskConfig: {
      copyMode: "ONE_TO_ONE"
    }
  }')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/batch-update" \
  -d "$PAYLOAD"

Batch update guidance:

  • status
    uses the same allowed values as single-task update:
    RUNNING
    ,
    PAUSED
    ,
    STOPPED
    .
  • taskConfig
    replaces the task config object sent in the batch request, so only send fields you intentionally want to set.
  • If neither
    status
    nor
    taskConfig
    is provided, the backend currently just returns the selected tasks.
  • Use batch update only when the same change should apply to all listed tasks.

Action: Batch delete tasks

  • POST /api/v1/copy-tasks/batch-delete
  • Required:
    taskIds
    (array of task IDs)

Required flow before batch delete:

  1. Validate all
    taskIds
    .
  2. For each task, check
    GET /api/v1/copy-tasks/{taskId}/positions?status=active
    .
  3. If a task has active positions, include it in the confirmation summary and state that those positions will be cleared before deletion.
  4. After explicit confirmation, run
    POST /api/v1/copy-tasks/{taskId}/sell-all
    for each task that still has active positions.
  5. After the position cleanup passes finish, call batch delete once with all
    taskIds
    .

Before calling: confirm the full cleanup-then-delete operation.

Minimum fields to ask:

  • Always ask:
    taskIds

Example: pre-check one task's active positions

curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID/positions?status=active"

Example: clear positions for one task before batch delete

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/$TASK_ID/sell-all"
TASK_IDS=("64f0c7e7b8e4f8c1a1b2c3d4" "64f0c7e7b8e4f8c1a1b2c3d5")
PAYLOAD="$(jq -n \
  --argjson taskIds "$(printf '%s\n' "${TASK_IDS[@]}" | jq -R . | jq -s .)" \
  '{taskIds: $taskIds}')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/batch-delete" \
  -d "$PAYLOAD"

Guidance:

  • There is no batch position-delete endpoint; clean positions task by task with
    sell-all
    , then call
    batch-delete
    .
  • If some
    sell-all
    calls skip positions, report that in the confirmation/result summary and continue unless the user explicitly asks to stop.

Copy Signals

Action: List copy signals

  • GET /api/v1/copy-signals
  • Query params:
    • limit
      (int, default 50)
    • cursor
      (int, offset for pagination)
    • since
      (RFC3339 timestamp)
    • sinceCreatedAt
      (RFC3339 timestamp)
    • action
      (one of:
      COPIED
      ,
      SKIPPED
      ,
      FAILED
      ,
      RECEIVED
      )
    • trader
      (address filter)
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-signals?limit=20&action=COPIED"

Prefer composing the query from user intent. Only pass filters the user asked for.

Validation:

  • limit
    must be a positive integer.
  • cursor
    must be a non-negative integer.
  • since
    and
    sinceCreatedAt
    must be RFC3339 or RFC3339Nano timestamps.
  • Valid
    action
    values are:
    COPIED
    ,
    SKIPPED
    ,
    FAILED
    ,
    RECEIVED
    .

Action guidance:

  • RECEIVED
    : the signal was ingested.
  • COPIED
    : the copy order was placed successfully.
  • SKIPPED
    : the signal was intentionally not copied because checks or limits blocked it.
  • FAILED
    : the system attempted processing but failed.

Example: fetch signals since a specific time

curl -sS --fail-with-body "${AUTH[@]}" \
  "$BASE/api/v1/copy-signals?since=2026-03-01T00:00:00Z&limit=50"

Example: fetch next page using cursor

curl -sS --fail-with-body "${AUTH[@]}" \
  "$BASE/api/v1/copy-signals?cursor=50&limit=50"

Action: Stream copy signals

  • GET /api/v1/copy-signals/stream
  • Response type: Server-Sent Events (SSE)
curl -sS -N "${AUTH[@]}" "$BASE/api/v1/copy-signals/stream"

Stream guidance:

  • Use this only when the user explicitly wants real-time signal updates.
  • Expect events such as
    ready
    and
    copy_signal
    .
  • Keep the connection open; this is not a one-shot request.

Action: Get signal stats

  • GET /api/v1/copy-signals/stats

Returns signal counts grouped by action and by trader.

curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-signals/stats"

TPSL Rules (Take-Profit / Stop-Loss)

Action: Get TPSL rule

  • GET /api/v1/copy-tasks/{taskId}/tpsl-rules/{ruleId}

Minimum fields to ask:

  • Always ask:
    taskId
    ,
    ruleId
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID/tpsl-rules/$RULE_ID"

Action: Simulate TPSL rule

  • POST /api/v1/copy-tasks/{taskId}/tpsl-rules/{ruleId}/simulate

Minimum fields to ask:

  • Always ask:
    taskId
    ,
    ruleId
curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/$TASK_ID/tpsl-rules/$RULE_ID/simulate"

TPSL inline payload guidance

When creating a task,

tpslRules
items may include:

  • sourceOrderId
    ,
    sourceType
  • Optional entry info:
    conditionId
    ,
    tokenId
    ,
    assetId
    ,
    entryPrice
    ,
    entrySize
    ,
    side
  • Optional risk config:
    takeProfitPct
    ,
    takeProfitProb
    ,
    stopLossPct
    ,
    limitPriceOffset
    ,
    orderTimeout
    ,
    fallbackToMarket

When updating a task,

tpslRules
should be wrapped as:

  • create
    : array of new inline rules
  • update
    : array of
    {id, ...patchFields}
  • cancel
    : array of
    ruleId

Example: create a task with one TPSL rule

PAYLOAD="$(jq -n \
  --arg targetTrader "0x..." \
  --arg sourceOrderId "order_123" \
  --arg sourceType "copy_trade" \
  --argjson takeProfitPct 20 \
  --argjson stopLossPct 10 \
  '{
    targetTrader: $targetTrader,
    tpslRules: [
      {
        sourceOrderId: $sourceOrderId,
        sourceType: $sourceType,
        takeProfitPct: $takeProfitPct,
        stopLossPct: $stopLossPct
      }
    ]
  }')"

Error handling

  • 401
    : API key missing/invalid/expired/disabled. Ask user to check or regenerate key.
  • 404
    : Task or resource not found. Ask user to verify
    taskId
    .
  • 409
    : Task already exists (duplicate
    targetTrader
    ).
  • 422
    : Copy task limit exceeded.
  • 400
    : Invalid payload or invalid query param such as
    includeDeleted
    .
  • 5xx
    : Server error. Retry once with backoff; if still failing, report response body.