Skip to main content
# x402 Agent Spec (Battle Trade)

**Status:** Pre-1.0. Accepted for the EasyA hackathon cohort at Consensus
Miami 2026. Will converge with upstream Coinbase x402 once the protocol
spec lands. Until then, treat this doc as the source of truth for what
battle.fyi will accept.

## TL;DR

Upload JSON to `POST /api/agents/upload`. Your body can be either:

1. **Native** — Battle Trade's own agent config:
   ```json
   { "profile_id": "...", "config": <AgentConfig> }
   ```

2. **x402 envelope** — wrap the same config in a protocol-aware shell:
   ```json
   {
     "profile_id": "...",
     "x402": {
       "x402_version": "0.1",
       "agent": {
         "name": "MomentumBull",
         "description": "Long BTC when 5m change > 1%",
         "config": <AgentConfig>
       },
       "metadata": { "author_wallet": "0x...", "license": "MIT" }
     }
   }
   ```

Either form ends up in the same `agents` table. Envelope metadata is
preserved under `config.x402_metadata` for audit and leaderboard
display.

## AgentConfig schema

```ts
{
  name: string;             // 2-40 chars
  description: string;      // up to 200 chars
  version: number;          // integer, you bump on each publish
  species?: string;         // creature avatar: bull|bear|shark|hawk|wolf|fox|snake|dragon|turtle|octopus|phoenix|owl
  rules: AgentRule[];       // 1-20 rules, evaluated top-to-bottom each tick
  risk: {
    max_positions: number;     // 1-10
    max_leverage: number;      // 1-50
    stop_loss_pct: number;     // -50 to 0
    take_profit_pct: number;   // 0 to 100
  };
}
```

### Rule grammar

```ts
{
  condition: string;  // e.g. "price_change_5m > threshold"
  action: "open_long" | "open_short" | "close_all" | "close_longs" | "close_shorts" | "reduce_position";
  asset?: "BTC" | "ETH" | "SOL" | ...;  // see VALID_ASSETS
  size?: "small" | "medium" | "large" | "max";
  leverage?: number;  // capped by risk.max_leverage
}
```

Built-in condition tokens:

- `price_change_1m > threshold` (and `5m`, `15m`, `<`)
- `unrealized_pnl_pct > threshold` / `< threshold`
- `position_count > threshold` / `< threshold`

Custom conditions are accepted as opaque strings — the executor will
ignore unknown tokens rather than crash.

## x402 envelope fields

| Field              | Required | Notes                                    |
|--------------------|----------|------------------------------------------|
| `x402_version`     | yes      | Semver string. We currently accept "0.x".|
| `agent.name`       | no       | Falls back to `config.name`.             |
| `agent.description`| no       | Falls back to `config.description`.      |
| `agent.config`     | yes      | Native `AgentConfig` (see above).        |
| `metadata`         | no       | Free-form. Preserved verbatim in DB.     |

## Example

```json
{
  "profile_id": "prof_xxx",
  "x402": {
    "x402_version": "0.1",
    "agent": {
      "name": "BreakoutHunter",
      "description": "5m momentum longs, tight stop",
      "config": {
        "name": "BreakoutHunter",
        "description": "5m momentum longs, tight stop",
        "version": 1,
        "species": "hawk",
        "rules": [
          { "condition": "price_change_5m > threshold", "action": "open_long", "asset": "BTC", "size": "medium", "leverage": 5 },
          { "condition": "unrealized_pnl_pct < threshold", "action": "close_all" }
        ],
        "risk": {
          "max_positions": 3,
          "max_leverage": 10,
          "stop_loss_pct": -5,
          "take_profit_pct": 15
        }
      }
    },
    "metadata": {
      "author_wallet": "0xabcd...",
      "x402_payment_ref": "0xpayment..."
    }
  }
}
```

## Limits

- Max 10 agents per profile
- 1-20 rules per agent
- Leverage capped at 50x (further capped per-lobby)
- Position count capped at 10

## Getting a gate code

During Miami, Bot Arena access is gated. Hackathon participants should
enter `HACKATHON_DEVS`. See #base-miami-trading-comp Telegram thread
for the current valid codes.

## Upload reference

### Request

```bash
curl -X POST https://battle.fyi/api/agents/upload \
  -H "Content-Type: application/json" \
  -d @your-agent.json
```

### Response — success (201)

```json
{
  "agent": {
    "id": "agt_...",
    "profile_id": "prof_...",
    "name": "BreakoutHunter",
    "description": "5m momentum longs, tight stop",
    "config": { /* normalized AgentConfig with x402_metadata preserved */ },
    "version": 1,
    "status": "draft",
    "wins": 0,
    "losses": 0,
    "total_battles": 0,
    "created_at": "2026-05-05T14:30:00Z"
  }
}
```

### Response — validation failure (422)

```json
{
  "error": "Invalid agent config",
  "details": [
    "name too long (max 40 characters)",
    "rules[0].asset \"UNICORN\" not supported (allowed: BTC, ETH, SOL, ...)",
    "risk.max_leverage must be 1-50"
  ]
}
```

### Response — request too large (413)

```json
{ "error": "Request too large (max 102400 bytes)" }
```

### Response — rate limited (429)

```json
{ "error": "Too many uploads, slow down" }
```
Includes `Retry-After: 60` header. Limit is 10 uploads / minute / IP.

### Response — malformed JSON (400)

```json
{ "error": "Invalid JSON body" }
```

## Body size + rate limits

| Limit | Value |
|-------|-------|
| Max body size | 100 KB |
| Uploads per IP per minute | 10 |
| Max agents per profile | 10 |
| Max `x402_metadata` size | 5 KB (JSON-stringified) |
| Max `rules[].condition` length | 200 characters |
| Max `name` length | 40 characters |
| Max `description` length | 200 characters |

## Competing in Bot Arena

Once uploaded, your agent is in `draft` status. To enter a Bot Arena lobby:

1. Visit `https://battle.fyi/agents` (need `HACKATHON_DEVS` gate code)
2. Pick your agent → deploy → enter a Bot Arena lobby
3. Your bot trades automatically for the round duration
4. Scores show on the live leaderboard at `/lobby/[id]/wall`

Agents are paper-only — no real capital at stake. Scored by final PnL %.

## Questions

Battle Trade side: DM @ellisnorman_ on TG.
Upstream x402 spec: follow Coinbase Developer Platform docs.