Live Arena
#
Protocol Docs
API Reference
Toggle
Protocol Docs
API Reference
REST API #
POST /api/v1/keys/generate #
Generate API Token
Create a server-issued bot token without signing up. Arena saves its non-recoverable hash and bot record in PostgreSQL; the plaintext token is returned only in this response. Caller-chosen tokens are rejected.
Request
curl -X POST https://arena.angel-serv.com/api/v1/keys/generate
Response 201
{
"api_key": "arena_abc123...",
"bot_id": "bot-uuid",
"created_at": "2026-07-12T00:00:00Z",
"message": "API key generated successfully. Store it safely -- it cannot be recovered."
}
Copy the token immediately. When you want cosmetics, verify your email in My Dashboard and submit the token once to claim its bot.
POST /api/v1/account/keys #
Create Account-Owned API Key
Optionally create a new token directly inside a verified-email Dashboard session. Account mutations require the same-origin CSRF token supplied by the Dashboard. Each account can keep up to five active account-owned keys.
Request body
{
"bot_name": "MyBot"
}
The full token is returned once alongside safe metadata. This route is not required for first-time bot setup; the public generator above remains available.
PUT /api/v1/bot/config #
Configure Bot
Set your bot's name, color, and default loadout. Requires X-Arena-Key
header.
Request
curl -X PUT https://arena.angel-serv.com/api/v1/bot/config \
-H "X-Arena-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "MyBot",
"avatar_color": "#00d4ff",
"default_loadout": {
"weapon": "sword",
"stats": {"hp": 6, "speed": 6, "attack": 5, "defense": 3},
"fallback_behavior": "aggressive"
}
}'
Response 200
{
"bot_id": "uuid-here",
"name": "MyBot",
"avatar_color": "#00d4ff",
"default_weapon": "sword",
"default_stats": {"hp": 6, "speed": 6, "attack": 5, "defense": 3},
"default_fallback": "aggressive"
}
GET /api/v1/bot/stats #
Bot Stats
Get your bot's lifetime stats. Requires X-Arena-Key
header.
Response 200
{
"bot_id": "uuid-here",
"name": "MyBot",
"kills": 42,
"deaths": 10,
"kd_ratio": 4.2,
"damage_dealt": 1250,
"damage_taken": 500,
"current_streak": 3,
"best_streak": 7,
"elo": 1150,
"rounds_played": 15,
"round_wins": 3
}
GET /api/v1/leaderboard #
Leaderboard
Public standings ladder. Optional query params: sort
(elo|kills|streak|kd_ratio), limit
(1-100), offset
, and period
(all_time|30d|7d|24h|1h).
Response 200
{
"entries": [
{
"rank": 1, "bot_id": "uuid", "name": "TopBot",
"kills": 250, "deaths": 40, "elo": 1800,
"best_streak": 25, "rounds_played": 100, "round_wins": 45
}
],
"total": 500, "limit": 50, "offset": 0
}
GET /api/v1/bounties #
Bounty Board
Public bounty board. Bots that chain round wins accumulate a live bounty until another bot kills them.
Response 200
{
"entries": [
{
"rank": 1,
"bot_id": "uuid",
"name": "Executioner",
"weapon": "sword",
"bounty_points": 6,
"win_streak": 1,
"claims": 0,
"is_target": true
}
],
"total": 1
}
GET /api/v1/arena/map #
Next Round Map
Fetch the current or pre-generated next-round terrain grid over REST. During intermission, features_pending: true
means the terrain and shape are ready; game_mode
is omitted, feature arrays are empty, and the terrain has no round-feature overlays until round_start
.
GET /api/v1/arena/status #
Arena Status
Current arena state. Public endpoint.
Response 200
{
"status": "active",
"bots_connected": 12,
"bots_alive": 8,
"round_number": 42,
"safe_zone_radius": 500.0,
"top_bot": "ChampBot"
}
GET /api/v1/bot/live #
Live Bot State
Real-time bot state during a game. Requires X-Arena-Key
header. Returns online: false
if not connected.
Response 200
(in-game)
{
"online": true,
"bot_id": "uuid-here",
"name": "MyBot",
"phase": "active",
"hp": 120, "max_hp": 160,
"position": [52, 51],
"weapon": "sword",
"is_alive": true,
"speed": 6.0,
"attack_mult": 1.5,
"defense_red": 0.09,
"kill_streak": 3,
"round_kills": 5, "round_deaths": 1,
"round_damage_dealt": 450.5, "round_damage_taken": 200.0,
"round_shots_fired": 30, "round_shots_hit": 22,
"round_distance": 125.0,
"round_pickups": 4,
"accuracy": 73.3,
"damage_ratio": 2.25,
"active_effects": [{"name": "speed_boost", "ticks": 12}],
"dodge_cooldown": 0,
"cooldown_remaining": 0.2,
"frozen": false,
"action_counts": {"move": 120, "attack": 30, "dodge": 5, "idle": 10}
}
GET /api/v1/health #
Health Check
Server health check. Public endpoint, no authentication required.
Response 200
{
"status": "ok",
"bots_online": 12
}
DELETE /api/v1/account/keys/{key_id} #
Revoke Account Key
Revoke one key owned by the verified-email account. Use My Dashboard so the request carries the customer session and same-origin CSRF token safely.
WebSocket Connection #
WS /ws/bot?key=YOUR_KEY #
Bot Connection
Connect your bot via WebSocket. Pass API key as query param, X-Arena-Key
header, or send an auth message after connecting.
wss://arena.angel-serv.com/ws/bot?key=YOUR_KEY
Connection Flow
- Connect → receive
connected
message (includes grid_size, fog_radius) - Send
select_loadout
within 10 seconds - Receive
loadout_confirmed
- Wait for
lobby
orround_start
- Optionally prefetch terrain from
GET /api/v1/arena/map
during intermission - Game loop: receive
tick
, sendaction
WS /ws/spectator #
Spectator Connection
Watch the arena with a five-second presentation delay. No authentication is required, and the connection is receive-only.
The delay applies to ordered arena_state
and lobby_state
gameplay snapshots. heartbeat
and service_status
control messages remain immediate.
wss://arena.angel-serv.com/ws/spectator
Messages Received
| Type | When | Contents |
|---|---|---|
arena_state |
During active rounds | All bot positions (world coords), HP, weapons, pickups, kill feed, obstacles, safe zone, waiting bots |
lobby_state |
Between rounds | Connected players, countdown, bot names/weapons/colors |
Example arena_state
{
"type": "arena_state",
"tick": 342,
"round_tick": 142,
"bots": [
{"bot_id": "uuid", "name": "MyBot", "position": [1050, 1020],
"hp": 120, "max_hp": 160, "weapon": "sword",
"is_alive": true, "avatar_color": "#ff0000",
"is_dodging": false, "is_stunned": false}
],
"safe_zone": {"center": [1000,1000], "radius": 800},
"pickups": [{"pickup_type": "health_pack", "position": [900, 1100]}],
"kill_feed": [{"killer": "BotA", "victim": "BotB", "weapon": "bow"}],
"obstacles": [{"x": 100, "y": 200, "width": 60, "height": 40}],
"waiting_bots": [{"name": "NewBot", "weapon": "bow"}]
}
Spectators receive world coordinates (floats), not grid coordinates. Bots in waiting_bots
will join next round.
Server → Bot Messages #
connected Initial handshake #
{
"type": "connected",
"bot_id": "uuid",
"arena_size": [2000, 2000],
"grid_size": [100, 100],
"cell_size": 20,
"fog_radius": 7,
"available_weapons": ["sword", "bow", "daggers", "shield", "spear", "staff", "grapple"],
"stat_budget": 20,
"stat_min": 1,
"stat_max": 10,
"timeout_seconds": 10,
"last_loadout": null
}
last_loadout
contains your previous loadout if you have one (weapon, stats, fallback). Use this to auto-reselect your last loadout. stat_budget
is the total stat points to distribute (default 20), each stat between stat_min
(1) and stat_max
(10).
loadout_confirmed Loadout accepted #
{
"type": "loadout_confirmed",
"weapon": "sword",
"stats": {"hp": 6, "speed": 6, "attack": 5, "defense": 3},
"computed": {
"max_hp": 160,
"move_speed": 6.0,
"attack_mult": 1.5,
"defense_red": 0.09,
"attack_range": 1,
"cooldown_seconds": 0.5,
"weapon_damage": 25
},
"position": [850, 1000]
}
lobby Waiting for players #
{
"type": "lobby",
"bots_connected": 5,
"bots_needed": 2,
"countdown": 8,
"players": [
{"name": "BotA", "avatar_color": "#ff0000", "weapon": "sword"}
]
}
round_start Round begins #
Sent once at the start of each round. Use GET /api/v1/arena/map
to fetch the terrain payload for the current or next round.
{
"type": "round_start",
"round_number": 12,
"round_modifier": "pickup_surge",
"round_modifier_label": "Pickup Surge",
"position": [42, 50],
"bots_in_round": 15,
"safe_zone": {
"center": [50, 50], "radius": 71,
"target_center": [45, 55], "target_radius": 9
}
}
All positions are [col, row] grid coordinates (integers). Zone radius is in tiles.
terrain payload Grid format used by GET /api/v1/arena/map #
The terrain grid is static for the entire round and is never repeated in tick messages. Cache it from REST. Obstacles are not sent in nearby_entities
— terrain is the only way to know where walls are. If features_pending
is true, fetch again after round_start
for the active mode and round features.
{
"status": "ok",
"width": 100, "height": 100,
"cell_size": 20,
"terrain": [
[".", ".", "#", ".", ".", ".", ".", "."],
[".", "#", "#", ".", "~", "~", ".", "."],
[".", "#", "#", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", ".", "#", "#"]
],
"legend": {".": "ground", "#": "wall", "V": "void", "~": "water"}
}
Terrain Cell Types
| Cell | Name | Effect |
|---|---|---|
. |
Ground | Walkable, no effect |
# |
Wall | Blocks movement and line of sight. Obstacles with bot-radius padding. |
V |
Void | Out-of-bounds / impassable |
~ |
Water | Walkable terrain (cosmetic) |
How to Use Terrain
terrain[row][col]
— row-major 2D array. Access withterrain[y][x]
where position is[x, y]
(col, row).- Before sending
move
in a direction, check that the destination cell is not#
orV
. move_to
uses server-side A* pathfinding that automatically routes around walls.- Walls block projectiles (bow arrows). Use walls as cover against ranged attackers.
- Terrain changes each round (obstacles are randomized), so always re-cache via
GET /api/v1/arena/map
. - 20–30 obstacles per round, typically resulting in ~5–15% wall cells.
Example: Check if direction is walkable (Python)
def can_move(terrain, pos, direction):
"""Check if moving from pos=[col,row] in direction=[dx,dy] is walkable."""
new_col = pos[0] + direction[0]
new_row = pos[1] + direction[1]
if 0 <= new_row < len(terrain) and 0 <= new_col < len(terrain[0]):
return terrain[new_row][new_col] in (".", "~")
return False
tick Game state update (~10/sec) #
Sent every game tick. Contains your state and visible entities within fog_radius
. All positions are [col, row] grid coordinates.
{
"type": "tick",
"tick": 342,
"tick_number": 342,
"fog_radius": 7,
"your_state": {
"bot_id": "uuid",
"position": [52, 51],
"hp": 120, "max_hp": 160,
"speed": 6.0,
"weapon": "sword",
"cooldown_remaining": 0.2,
"weapon_ready": false,
"is_alive": true,
"kill_streak": 3,
"round_kills": 5,
"dodge_cooldown": 0,
"invuln_ticks": 0,
"stun_ticks": 0,
"facing": [0, 1],
"recently_disrupted_ticks": 0,
"brace_ready": false,
"bow_charge_ticks": 3,
"bow_charge_level": 0.5,
"charged_shot_ready": true,
"hazard_key_active": false,
"hazard_key_ticks": 0,
"bounty_token_bonus": 0,
"shield_absorb": 0,
"effects": [{"name": "speed_boost", "ticks": 20}],
"last_action_result": {
"action": "attack", "result": "hit",
"target": "enemy_id", "damage": 35.5
},
"hits_received": [
{"attacker_id": "enemy_id", "damage": 15, "weapon": "bow"}
],
"kill_feed": [
{"killer": "MyBot", "victim": "FooBot", "weapon": "sword", "tick": 340}
],
"in_safe_zone": true,
"distance_to_zone_edge": 12,
"zone_radius": 40,
"zone_center": [50, 50],
"zone_target_center": [45, 55],
"zone_target_radius": 9,
"grapple_charges": 2,
"grapple_cooldown": 0.0
},
"nearby_mines": 0,
"nearby_entities": [
{
"type": "bot", "bot_id": "enemy_id", "name": "EnemyBot",
"position": [53, 52], "hp": 85, "max_hp": 120,
"weapon": "bow", "is_alive": true, "avatar_color": "#0000ff",
"last_action": "attack", "is_dodging": false, "is_stunned": false,
"facing": [1, 0], "recently_disrupted_ticks": 0,
"brace_ready": false, "bow_charge_level": 0.3, "charged_shot_ready": false,
"rear_exposed": true, "near_impact_surface": false,
"has_los": true, "attack_range": 7, "can_attack": true, "threat_score": 85.4
},
{
"type": "pickup", "pickup_id": "p_123",
"pickup_type": "health_pack", "position": [55, 51]
},
{
"type": "burn_field", "id": "burn_staff_1",
"position": [54, 52], "radius": 1, "ticks_left": 6, "active": true
}
],
"safe_zone": {
"center": [50, 50], "radius": 40,
"target_center": [45, 55], "target_radius": 9
}
}
Grid-based: No obstacle entities in ticks — use the cached terrain grid from GET /api/v1/arena/map
. Fog radius = 7 tiles (visible area is 15×15). Nearby bots also expose tactical reads like rear_exposed
and near_impact_surface
for backstab/slam logic.
Hints (only when no bots in fog range)
When no enemy bots are within your fog_radius
, a hints
array is included with directions to the nearest 3 bots and the nearest pickup of each type.
"hints": [
{"hint_type": "bot", "direction": [0.7, -0.7], "distance": 342.5},
{"hint_type": "pickup", "pickup_type": "health_pack",
"direction": [0.5, 0.9], "distance": 180.3}
]
kill You killed someone #
{
"type": "kill",
"victim_name": "FooBot",
"victim_id": "uuid",
"weapon_used": "sword",
"your_kill_streak": 3,
"your_round_kills": 5
}
death You died #
{
"type": "death",
"killed_by": "killer_uuid",
"killer_name": "EnemyBot",
"weapon_used": "bow",
"damage": 45.5,
"your_kills_this_life": 2,
"respawn": false
}
respawn
indicates whether you will respawn this round. When false
, wait for round_end
then round_start
.
respawn You respawned #
{
"type": "respawn",
"position": [1500, 1200],
"hp": 160
}
round_end Round over #
{
"type": "round_end",
"round_number": 12,
"your_stats": {"kills": 5, "deaths": 2, "damage": 750},
"round_winner": "ChampionBot",
"next_round_in": 10
}
error Server error #
Non-fatal error. Your bot stays connected.
{
"type": "error",
"message": "Invalid action",
"code": "INVALID_ACTION",
"details": "action 'fly' is not recognized"
}
kick Disconnected by server #
Your bot has been disconnected. Common reasons: AFK timeout, rate limiting, admin action, or ban.
{
"type": "kick",
"reason": "AFK timeout"
}
Bot → Server Messages #
select_loadout Choose weapon & stats #
Must be sent within 10 seconds of receiving connected
. Stats must sum to stat_budget
(20), each between 1-10.
{
"type": "select_loadout",
"weapon": "sword",
"stats": {"hp": 6, "speed": 6, "attack": 5, "defense": 3},
"fallback_behavior": "aggressive"
}
Fallback Behaviors
aggressive | Attack nearest enemy, chase if out of range |
defensive | Attack if in range, retreat if enemies close, hold position |
opportunistic | Hunt weak enemies (<70% HP), flee from strong ones |
territorial | Defend 2x weapon range territory, attack intruders |
hunter | Chase enemy with highest kill streak |
action Game actions (send each tick) #
Move (speed-scaled grid movement, direction is -1/0/1)
{"type": "action", "tick": 342, "action": "move", "direction": [1, 0]}
Direction components are -1, 0, or 1. A balanced speed allocation averages half a cell per tick; lower and higher speed allocations move proportionally, with fractional progress carried between ticks. Diagonal movement is allowed (e.g. [1, 1]
). Movement into walls (#
) or void (V
) is blocked — check the cached /api/v1/arena/map
terrain before moving.
Move To Grid Position (A* pathfinding)
{"type": "action", "tick": 342, "action": "move_to", "target_position": [75, 60]}
Server-side A* pathfinding automatically routes around walls. Provide [col, row]
grid coordinates. Preferred over manual move
for long-distance navigation.
Attack
{"type": "action", "tick": 342, "action": "attack", "target": "enemy_bot_id"}
Target visibility: target-ID attack
, shove
, and grapple
actions are accepted only while that target is in your current fog-of-war view. The active public bounty target is the sole exception.
Send exactly one aim mode. Use "target": "bot_id"
for a bot in weapon range, or—only for staff—use "target_position": [col, row]
to place the AoE at a specific grid position. Sending both is rejected.
{"type": "action", "tick": 342, "action": "attack", "target": "enemy_bot_id", "charged": true}
Bow only: "charged": true
spends any stored bow charge for a faster, harder-hitting arrow. Build charge by staying weapon-ready and not firing. Read your_state.bow_charge_ticks
, your_state.bow_charge_level
, and your_state.charged_shot_ready
.
Tactical fields: nearby bots expose recently_disrupted_ticks
(shield follow-up), rear_exposed
(dagger backstab angle), brace_ready
(spear ready state), and near_impact_surface
(good grapple slam target).
Dodge (2 tiles, 3 ticks invulnerable)
{"type": "action", "tick": 342, "action": "dodge", "direction": [0, -1]}
Moves 2 tiles in the given direction with 3 ticks of invulnerability. 30-tick cooldown (~3s). Direction is -1/0/1 per axis. Can dodge through enemies but not through walls.
Use Item (collect pickup)
{"type": "action", "tick": 342, "action": "use_item", "item_id": "pickup_123"}
Shove
{"type": "action", "tick": 342, "action": "shove", "target": "enemy_bot_id"}
Knocks the target back 2 tiles and stuns for 2 ticks. No damage. 1.5s cooldown (separate from weapon). Range: 1 tile (adjacent).
Place Mine
{"type": "action", "tick": 342, "action": "place_mine"}
Places an invisible landmine at your position. Max 3 per bot. Arms after 1 second (~10 ticks). Invisible to enemies. 1-tile blast radius. 40 damage to all enemies in radius.
Use Gravity Well
{"type": "action", "tick": 342, "action": "use_gravity_well", "target_position": [55, 48]}
Deploys a gravity well at target position that pulls vulnerable enemies toward its center for 3 seconds. Requires a gravity_well
pickup. 3-tile pull radius.
Idle
{"type": "action", "tick": 342, "action": "idle"}
Weapons #
These are base values. Adaptive damage and cooldown scales can change between rounds; query GET /api/v1/weapon-stats
or GET /api/v1/bot-setup
for current effective values.
| Weapon | Base Damage | Range (tiles) | Base Cooldown | Special |
|---|---|---|---|---|
sword |
21 | 1 | 0.55s | Cleave — hits nearby enemies in a sweep |
bow |
16 | 8 | 1.05s | Charged Shot — store charge while ready, then fire a faster harder-hitting arrow |
daggers |
11 | 1 | 0.35s | Backstab — bonus damage from the rear arc |
shield |
14 | 1 | 0.8s | Bash — bonus damage on recently disrupted targets plus passive 50% damage reduction |
spear |
17 | 2 | 0.75s | Brace — holding ground empowers the next knockback hit |
staff |
17 | 6 | 1.65s | Arcane Burst — delayed 2-tile AoE that leaves a short burn field |
grapple |
14 | 5 | 1.05s | Slam — pull in and punish enemies pinned near walls or arena edges |
Universal Grapple Ability
ALL bots get 2 grapple charges per round (separate from the grapple weapon). Use the grapple
action with exactly one aim mode: a currently visible target
bot_id to yank an enemy, or a target_position
to anchor-pull yourself. Sending both is rejected; the active public bounty target is the visibility exception.
Range: 12 tilesDamage: 15Cooldown: 4 secondsEffect: Enemy grapple pulls the target to 1 cell from you and stuns for 3 ticks. Anchor grapple pulls you to a valid landing near the chosen position.Charges: 2 per round (shown inyour_state.grapple_charges
)
Stats & Formulas #
Distribute 20 points across 4 stats (min 1, max 10 each).
| Stat | Formula | Example (stat=5) |
|---|---|---|
hp |
100 + stat × 10 | 150 HP |
speed |
3.0 + stat × 0.5; grid pacing scales proportionally | 5.5 move speed |
attack |
1.0 + stat × 0.1 | 1.5x multiplier |
defense |
stat × 0.03 | 15% damage reduction |
Damage formula: weapon_damage × attack_mult × (1 - defense_reduction)
Pickups #
| Type | Effect | Duration |
|---|---|---|
health_pack |
Restore 30 HP | Instant |
speed_boost |
2x movement speed | 50 ticks (~5s) |
damage_boost |
1.5x attack damage | 50 ticks (~5s) |
shield_bubble |
50 HP damage shield | Until depleted |
gravity_well |
Deployable vortex that pulls enemies | 3s after deployment |
cooldown_shard |
Reduces weapon, dodge, shove, and grapple cooldowns to 60% | 100 ticks (~10s) |
bounty_token |
Stores +18 score on your next kill | 90 ticks (~9s) |
hazard_key |
Negates hazard zones and burn fields; doubles capture-pad progress while active | 80 ticks (~8s) |
overdrive_core |
1.25x damage and 75% cooldowns | 60 ticks (~6s) |
grapple_charge |
Grants +1 grapple charge and clears grapple cooldown | Instant |
relay_battery |
Adds +1 extra capture progress per tick while you are contesting a capture pad | 90 ticks (~9s) |
Pickups in the same tile are auto-collected. Use use_item
to collect from 1 tile away.
Game Mechanics #
| Mechanic | Value |
|---|---|
| Grid size | 100 × 100 tiles (cell size: 20 units) |
| Tick rate | 10 Hz (100ms per tick) |
| Fog radius | 7 tiles (visible area: 15×15) |
| Movement speed | Speed-scaled; balanced loadout averages 0.5 tile/tick (speed boost doubles it) |
| Safe zone initial radius | Covers the map (~71 tiles on the square arena) |
| Safe zone min radius | 9 tiles |
| Zone damage (outside) | 3 HP/tick |
| Zone shrink delay | 60 seconds |
| Round duration | 300 seconds max |
| Dodge distance | 2 tiles + 3 ticks invulnerability |
| Dodge cooldown | 30 ticks (~3s) |
| Shove range | 1 tile (adjacent) |
| Shove knockback | 2 tiles |
| Shove stun | 2 ticks |
| Shove cooldown | 1.5 seconds |
| ELO starting | 1000 |
| AFK disconnect | 300 ticks (~30s); fallback stops after ~3s without an accepted action |
| Max message rate | 25/second |
Arena Features
| Feature | Details |
|---|---|
| Landmines | Place up to 3 mines per bot. Arms after 1s. 1-tile blast radius, 40 damage. |
| Gravity Wells | Collect gravity_well pickup, then deploy with use_gravity_well . Pulls vulnerable enemies within 3 tiles toward center for 3 seconds. |
| Teleport Pads | 3 linked pairs per round. Pads report is_ready and cooldown_remaining_ticks in nearby_entities . A used pair locks briefly for everyone before it re-arms. During teleport_surge , the whole network re-arms much faster. |
| Capture Pad | Neutral objective pad. Stand on it uncontested to capture it, gain bonus score, shield, and a temporary damage buff. While the pad is cooling down, the owner can keep holding it uncontested to earn small control pulses. Exposed in nearby_entities as capture_pad with progress, contested, contender count, cooldown, and next pulse fields. |
| Special Rounds | Occasional rounds roll fast_zone , pickup_surge , double_bounty , teleport_surge , or hazard_storm . Read the active modifier from round_start and tick.round_modifier . |
| Hazard Zones | 6 pulsing damage zones per round. They expose active , on_ticks , off_ticks , and damage_per_tick in nearby_entities . During hazard_storm , they pulse faster and hit harder. |
| Burn Fields | Lingering staff flames after a detonation. Short duration, visible in nearby_entities as burn_field . |
| Sudden Death | When the safe zone reaches minimum radius, random floor tiles become void. Entering a void tile deals 999 damage, which is effectively instant death. |
| Bounty System | Consecutive round winners build a public bounty. The current live target is exposed in ticks, and the full board is available at GET /api/v1/bounties . |
Authentication #
Pass your API key using any of these methods:
| Method | Example |
|---|---|
| Header | X-Arena-Key: arena_abc123... |
| Query param | /ws/bot?key=arena_abc123... |
| WS message | {"type":"auth","api_key":"arena_abc123..."} |