Agent Setup

Connect your coding agent to fisshing for in-game notifications and a live spinner on the map.

1

Get your key

Find it in-game at Profile → API Key

2

Configure your agent

Add hooks for Claude Code, Cursor, or OpenCode

3

Code away

Get notified and show your spinner automatically

Your API Key

Your API key is a unique fsh_-prefixed token derived from your session. Find it in-game at Profile → API Key.

Authorization: Bearer fsh_xxxxxxxxxxxx

API Reference

Full documentation for every endpoint

Notifications

Send in-game notifications and toggle the map spinner. These are the core endpoints used by all agent integrations.

POST /api/notify

Send a notification to yourself in-game (max 80 chars). Shows as a chat bubble and status bar message.

curl -X POST https://fisshing.net/api/notify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Build complete!", "source": "my-agent"}'

Parameters

message
required — string, max 80 characters
source
optional — string, max 20 chars. Prefixed to the message display
alert_level
optional — one of success, info, warning, error, record
dismiss_interval
optional — integer, 1–30000 ms. Auto-dismiss time (default 5000)

POST /api/status

Toggle an animated spinner next to your name on the map, visible to all players.

curl -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": true}'

Deactivate with: -d '{"active": false}'

Parameters

active
required — boolean. true to show spinner, false to hide

Player API

Read your game data even while offline. Uses the same API key from your profile.

GET /api/state

Returns your gold, location, skills, and totals. Works online and offline.

curl https://fisshing.net/api/state \
  -H "Authorization: Bearer YOUR_API_KEY"
Response shape
{"online": true, "gold": 1250, "location": "marina", "rod": "carbon_rod",
 "total_caught": 47, "total_gold": 3200,
 "skills": {"fishing": {"level": 12, "xp": 850}, "cooking": {"level": 3, "xp": 40}, ...}}

GET /api/fish

Returns your fish inventory with species, weight, rarity, and value.

curl https://fisshing.net/api/fish \
  -H "Authorization: Bearer YOUR_API_KEY"
Response shape
{"fish": [{"species": "bass", "name": "Bass", "weight": 4.2, "rarity": "common",
  "value": 15, "cooked": false, "burnt": false}, ...], "count": 5, "online": true}

Webhooks

Register URLs to receive HTTP POST notifications when game events happen. Max 3 per player.

POST /api/webhooks

Create a webhook. Provide a URL and a list of event types.

curl -X POST https://fisshing.net/api/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hook", "events": ["catch", "level_up"]}'

GET /api/webhooks

List your registered webhooks (secrets are masked).

curl https://fisshing.net/api/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY"

DELETE /api/webhooks/:id

Remove a webhook by its ID.

curl -X DELETE https://fisshing.net/api/webhooks/wh_WEBHOOK_ID \
  -H "Authorization: Bearer YOUR_API_KEY"

Event types

catch species, weight, rarity
level_up skill, old/new level
achievement id, name, category
sell fish, value, gold_after
world_record species, name, weight
session_start / session_end
new_species first catch of species
challenge_result dev challenge outcome

Use ["*"] to subscribe to all events.

Payload & signature verification

POST your-url
Content-Type: application/json
X-Fisshing-Signature: sha256=<hex>

{"event": "catch", "player_id": "alice", "timestamp": "2025-...", "data": {...}}

Verify with HMAC-SHA256: compute HMAC(secret, body) and compare to the signature header.

Collection API

Lifetime catch stats, personal bests, and completion tracking.

GET /api/collection

Returns species counts, best weights, completion percentage, and earned achievements.

curl https://fisshing.net/api/collection \
  -H "Authorization: Bearer YOUR_API_KEY"

Stream Overlay WebSocket

Real-time game events over WebSocket for OBS browser source overlays. Connect from any language or a plain <script> tag — no framework required.

Connect

Open a standard WebSocket to:

wss://fisshing.net/socket/overlay?token=YOUR_API_KEY

Once connected, join the overlay:lobby channel by sending a JSON message:

[ref, ref, "overlay:lobby", "phx_join", ]

Game events arrive as game_event messages on that channel. All webhook event types are supported.

Catch event extras

Catch events include color (hex, mapped from rarity) and ascii_art from the fish catalog — perfect for rendering catch cards on stream.

Common #9ca3af
Uncommon #22c55e
Rare #3b82f6
Epic #a855f7
Legendary #f97316

OBS overlay example

Save as an HTML file, replace YOUR_API_KEY, and add it as a Browser Source in OBS.

Developer Challenges

Push custom quiz challenges into your game session via API. An NPC spawns on the map — walk over, press E, answer the quiz. Results fire a challenge_result webhook.

POST /api/challenge

Create a challenge. Max 1 pending per player.

curl -X POST https://fisshing.net/api/challenge \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "What does handleAuth return on 401?",
    "options": ["null", "{error: unauthorized}", "throws Error", "undefined"],
    "correct": 1,
    "npc_name": "Code Review Bot",
    "time_limit_seconds": 20
  }'

GET /api/challenge

Check the current challenge status.

curl https://fisshing.net/api/challenge \
  -H "Authorization: Bearer YOUR_API_KEY"

DELETE /api/challenge

Cancel a pending challenge.

curl -X DELETE https://fisshing.net/api/challenge \
  -H "Authorization: Bearer YOUR_API_KEY"

Webhook event: challenge_result

Fires when a challenge is answered (success/fail/timeout). Includes challenge_id, prompt, result, correct_answer, player_answer, and time_taken_ms.

Agent Integrations

Copy-paste configs for popular coding agents

Claude Code

.claude/settings.json

Add hooks to show the agent spinner while Claude is working, send a notification when it stops, and clear the spinner on exit.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "command",
            "command": "curl -s -X POST https://fisshing.net/api/status -H 'Authorization: Bearer YOUR_API_KEY' -H 'Content-Type: application/json' -d '{\"active\": true}'"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "curl -s -X POST https://fisshing.net/api/notify -H 'Authorization: Bearer YOUR_API_KEY' -H 'Content-Type: application/json' -d '{\"message\": \"Agent stopped\", \"source\": \"Claude Code\"}' && curl -s -X POST https://fisshing.net/api/status -H 'Authorization: Bearer YOUR_API_KEY' -H 'Content-Type: application/json' -d '{\"active\": false}'"
          }
        ]
      }
    ]
  }
}

OpenCode

.opencode/plugins/fisshing.js

Drop this plugin file into your project. Hooks into tool.execute.before, permission.asked, and session.idle events.

const API = "https://fisshing.net/api";
const TOKEN = "YOUR_API_KEY";
const headers = {
  Authorization: `Bearer ${TOKEN}`,
  "Content-Type": "application/json",
};

async function notify(message) {
  await fetch(`${API}/notify`, {
    method: "POST",
    headers,
    body: JSON.stringify({ message, source: "OpenCode" }),
  }).catch(() => {});
}

async function setStatus(active) {
  await fetch(`${API}/status`, {
    method: "POST",
    headers,
    body: JSON.stringify({ active }),
  }).catch(() => {});
}

export const Fisshing = async () => ({
  "tool.execute.before": async () => {
    await setStatus(true);
  },
  event: async ({ event }) => {
    if (event.type === "session.idle") {
      await notify("Agent stopped");
      await setStatus(false);
    }
    if (event.type === "permission.asked") {
      await notify("Attention required");
    }
  },
});

Cursor

.cursor/rules

Add a task in your rules or use Cursor's task runner to call the API after each agent action.

# In your .cursor/rules or task config:
# After agent completes a task:
curl -s -X POST https://fisshing.net/api/notify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Task complete!", "source": "Cursor"}'

# Activate spinner while working:
curl -s -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": true}'

# Deactivate when done:
curl -s -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": false}'

Generic / Any Agent

curl

Use plain curl from any script, hook, or CI pipeline.

Send notification

curl -X POST https://fisshing.net/api/notify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Build passed!", "source": "my-agent"}'

Activate spinner

curl -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": true}'

Deactivate spinner

curl -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": false}'

Ideas & Recipes

Creative ways to use the API beyond agent integrations

CI/CD Pipeline

Spinner while building, colored alerts on pass/fail

GitHub Actions
# .github/workflows/ci.yml — add to your job:
- name: Fisshing spinner on
  run: |
    curl -s -X POST https://fisshing.net/api/status \
      -H "Authorization: Bearer ${{ secrets.FISSHING_KEY }}" \
      -H "Content-Type: application/json" \
      -d '{"active": true}'

- name: Run tests
  run: npm test  # or: mix test, pytest, cargo test

- name: Fisshing report
  if: always()
  run: |
    if [ "${{ job.status }}" = "success" ]; then
      AL=success; MSG="CI passed"
    else
      AL=error; MSG="CI failed"
    fi
    curl -s -X POST https://fisshing.net/api/notify \
      -H "Authorization: Bearer ${{ secrets.FISSHING_KEY }}" \
      -H "Content-Type: application/json" \
      -d "{\"message\":\"$MSG\",\"source\":\"GitHub\",\"alert_level\":\"$AL\"}"
    curl -s -X POST https://fisshing.net/api/status \
      -H "Authorization: Bearer ${{ secrets.FISSHING_KEY }}" \
      -H "Content-Type: application/json" \
      -d '{"active": false}'

Git Hooks

Get notified with commit messages as you work

post-commit
#!/bin/sh
# Save as .git/hooks/post-commit and chmod +x
MSG=$(git log -1 --pretty=%s | cut -c1-60)
curl -s -X POST https://fisshing.net/api/notify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"message\":\"Committed: $MSG\",\"source\":\"git\"}"

Long Command Wrapper

Spinner + notification for any shell command

~/.bashrc
# Add to ~/.bashrc or ~/.zshrc
fsh() {
  curl -s -X POST https://fisshing.net/api/status \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"active": true}' > /dev/null
  "$@"
  EXIT=$?
  if [ $EXIT -eq 0 ]; then AL=success; else AL=error; fi
  curl -s -X POST https://fisshing.net/api/notify \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"message\":\"Done: $1\",\"source\":\"shell\",\"alert_level\":\"$AL\"}" > /dev/null
  curl -s -X POST https://fisshing.net/api/status \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"active": false}' > /dev/null
  return $EXIT
}
# Usage: fsh make build

Test Runner

Green/red alerts after your test suite runs

run-tests.sh
#!/bin/sh
# run-tests.sh — wraps your test command
OUTPUT=$(mix test 2>&1)  # or: pytest, npm test, cargo test
EXIT=$?
if [ $EXIT -eq 0 ]; then
  AL=success; MSG="Tests passed"
else
  AL=error; MSG="Tests failed"
fi
curl -s -X POST https://fisshing.net/api/notify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"message\":\"$MSG\",\"source\":\"tests\",\"alert_level\":\"$AL\"}"
echo "$OUTPUT"

Deploy Tracker

Spinner during deploy, extended notification on result

deploy.sh
#!/bin/sh
# deploy.sh — wrap your deploy command
KEY="YOUR_API_KEY"
curl -s -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": true}' > /dev/null

fly deploy  # or: kubectl apply, docker push, etc.
EXIT=$?

if [ $EXIT -eq 0 ]; then AL=success; MSG="Deploy succeeded"
else AL=error; MSG="Deploy failed"; fi
curl -s -X POST https://fisshing.net/api/notify \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d "{\"message\":\"$MSG\",\"source\":\"deploy\",\"alert_level\":\"$AL\",\"dismiss_interval\":10000}"
curl -s -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": false}' > /dev/null

Cron Job Monitor

Know when scheduled jobs succeed or fail

crontab
# Add to crontab (crontab -e):
0 */6 * * * /path/to/backup.sh && \
  curl -s -X POST https://fisshing.net/api/notify \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"message":"Backup complete","source":"cron","alert_level":"success"}' \
  || curl -s -X POST https://fisshing.net/api/notify \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"message":"Backup failed!","source":"cron","alert_level":"error"}'

Health Check

Get warned in-game when a service goes down

health-check.sh
#!/bin/sh
# health-check.sh — run via cron every 5 min
URL="https://your-app.example.com/health"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
if [ "$STATUS" != "200" ]; then
  curl -s -X POST https://fisshing.net/api/notify \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"message\":\"$URL returned $STATUS\",\"source\":\"health\",\"alert_level\":\"error\",\"dismiss_interval\":15000}"
fi

Pomodoro Timer

Focus sessions with in-game start/stop alerts

pomodoro.sh
#!/bin/sh
# pomodoro.sh — 25-min focus timer
curl -s -X POST https://fisshing.net/api/notify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message":"Focus session started","source":"pomodoro","alert_level":"info"}'
curl -s -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": true}' > /dev/null
sleep 1500
curl -s -X POST https://fisshing.net/api/notify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message":"Time is up! Take a break","source":"pomodoro","alert_level":"warning","dismiss_interval":30000}'
curl -s -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": false}' > /dev/null

Shell Prompt Hook

Auto-notify when long-running commands finish

~/.zshrc
# Add to ~/.zshrc — notifies after commands taking 30s+
__fsh_precmd() {
  local EXIT=$? ELAPSED=$(( SECONDS - ${_FSH_START:-$SECONDS} ))
  unset _FSH_START
  if [ $ELAPSED -ge 30 ]; then
    local AL=success; [ $EXIT -ne 0 ] && AL=error
    curl -s -X POST https://fisshing.net/api/notify \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d "{\"message\":\"Finished (${ELAPSED}s)\",\"source\":\"shell\",\"alert_level\":\"$AL\"}" > /dev/null &
  fi
}
__fsh_preexec() { _FSH_START=$SECONDS; }
autoload -Uz add-zsh-hook
add-zsh-hook precmd __fsh_precmd
add-zsh-hook preexec __fsh_preexec

Docker Build

Track container builds with spinner and alerts

docker-notify.sh
#!/bin/sh
# docker-notify.sh — wraps docker build
curl -s -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": true}' > /dev/null
docker build -t "$1" .
EXIT=$?
if [ $EXIT -eq 0 ]; then AL=success; MSG="Build done: $1"
else AL=error; MSG="Build failed: $1"; fi
curl -s -X POST https://fisshing.net/api/notify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"message\":\"$MSG\",\"source\":\"docker\",\"alert_level\":\"$AL\"}"
curl -s -X POST https://fisshing.net/api/status \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active": false}' > /dev/null
# Usage: ./docker-notify.sh myapp:latest

Advanced Configuration

Get the most out of your fisshing experience

In-Game Scripting

Write bot scripts at the Coding Bench to automate fishing, woodcutting, and more

Overview

The scripting system lets you write Elixir-flavored bot programs on Script Cartridges. Scripts react to game events with handlers, check conditions with guards, and perform actions automatically.

Getting Started

  1. Buy a House Key from the shop (requires fishing level 15+)
  2. Buy a Coding Bench and place it in your home
  3. Buy a Script Cartridge (500g each)
  4. Interact with the Coding Bench to open the script editor
  5. Write your script, press Ctrl+S to compile & save
  6. Go to a fishing spot (or any location), open cartridges, and activate your script

Script Structure

Every script is wrapped in a script "name" do ... end block containing one or more on :event do ... end handlers.

script "my_bot" do
  on :idle do
    cast()
  end

  on :hooked do
    hook()
  end

  on :reeling do
    if fish_pos() > zone_pos() do
      reel(:right)
    else
      reel(:left)
    end
  end

  on :caught do
    if inventory_full?() do
      walk_to(:shop)
    end
  end

  on :arrived do
    if near_shop?() do
      sell_all()
      walk_back()
    end
  end
end

Charge System

Every action costs charge. Your charge pool starts at 100 and regenerates 1 per minute (faster near a home campfire). When charge runs out, the script pauses until it recharges.

Events

Events trigger your on handlers. The runtime fires events automatically as the game state changes.

Event Triggered When Category
:idle Player is idle, not fishing or doing anything Fishing
:hooked A fish bites the line (no species/rarity info available) Fishing
:reeling Actively reeling in a fish Fishing
:caught Successfully caught a fish. Provides rarity, species, weight Fishing
:escaped Fish escaped the line Fishing
:chop_complete Successfully chopped a tree Skills
:fire_lit Successfully lit a fire Skills
:cook_complete Finished cooking a fish Skills
:skill_idle No skill action in progress (e.g. after a burn) Skills
:arrived Finished walking to a destination. Provides at (:shop or :return) Movement

Actions

Actions are commands your script executes. Each costs charge from your script pool.

Action What It Does Charge
cast() Casts the line at ~50% power 2
hook() Sets the hook when a fish bites 1
reel(:left) Moves the reel zone left or right. Use fish_pos() and zone_pos() to decide direction 1
release() Releases a hooked fish 0
sell_all() Sells all fish (must be near shop) 1
walk_to(:shop) Walks to the shop. Fires :arrived with at: :shop 2
walk_back() Walks back to where you were before walk_to. Fires :arrived with at: :return 2
move(:up) Moves one tile. Accepts :up :down :left :right 1
travel(:pond) Teleports to a location 5
cycle_bait() Switches to the next bait type 0
wait(ms) No-op delay (does nothing, charge-free) 0
chop() Chops an adjacent tree (requires axe equipped) 2
light_fire() Places logs and lights a fire (requires tinderbox + wood) 2
cook() Cooks the first raw fish on an adjacent fire 1

Guards & Conditions

Use guards in if blocks or on :event when clauses to check game state. Supports and, or, not, and comparison operators.

Guard Returns Description
inventory_full?() boolean True when inventory is at capacity
gold() integer Current gold balance
location() atom Current location (:marina, :pond, etc.)
rarity() atom Rarity of last catch (only available in :caught handler)
species() atom Species of last catch (only available in :caught handler)
weight() float Weight of last catch (only available in :caught handler)
level() integer Fishing level
bait() atom Currently equipped bait type
rod() atom Currently equipped rod
near_shop?() boolean True when within range of the shop
has_wood?() boolean True when carrying any wood
wood() integer Current wood count
has_tool?(:axe) boolean True if you own the tool. Accepts :axe or :tinderbox
near_tree?() boolean True when adjacent to a choppable tree
near_fire?() boolean True when adjacent to an active fire
has_raw_fish?() boolean True when carrying uncooked fish
fish_pos() float Fish indicator position on the reel bar (0.0–1.0, only in :reeling)
zone_pos() float Your reel zone position on the bar (0.0–1.0, only in :reeling)

Example Scripts

Copy these into your Coding Bench as starting points.

Basic Auto-Fisher

Casts, hooks, reels, and loops forever.

script "basic_fisher" do
  on :idle do
    cast()
  end

  on :hooked do
    hook()
  end

  on :reeling do
    if fish_pos() > zone_pos() do
      reel(:right)
    else
      reel(:left)
    end
  end
end

Auto-Sell Fisher

Walks to the shop when inventory is full, sells, walks back, and resumes fishing.

script "auto_sell_fisher" do
  on :idle do
    cast()
  end

  on :hooked do
    hook()
  end

  on :reeling do
    if fish_pos() > zone_pos() do
      reel(:right)
    else
      reel(:left)
    end
  end

  on :caught do
    if inventory_full?() do
      walk_to(:shop)
    end
  end

  on :arrived do
    if near_shop?() do
      sell_all()
      walk_back()
    end
    cast()
  end
end

Auto-Sell with Bait Cycling

Fishes, sells when full, and cycles bait between casts.

script "bait_cycle_fisher" do
  on :idle do
    cycle_bait()
    cast()
  end

  on :hooked do
    hook()
  end

  on :reeling do
    if fish_pos() > zone_pos() do
      reel(:right)
    else
      reel(:left)
    end
  end

  on :caught do
    if inventory_full?() do
      walk_to(:shop)
    end
  end

  on :arrived do
    if near_shop?() do
      sell_all()
      walk_back()
    end
    cast()
  end
end

Lumberjack Cook

Chops trees, lights fires, cooks fish — a full resource loop.

script "lumberjack_cook" do
  on :skill_idle do
    if has_tool?(:axe) and near_tree?() do
      chop()
    end
  end

  on :chop_complete do
    if wood() >= 5 and has_tool?(:tinderbox) do
      light_fire()
    end
  end

  on :fire_lit do
    if has_raw_fish?() do
      cook()
    end
  end

  on :cook_complete do
    if has_raw_fish?() and near_fire?() do
      cook()
    else
      chop()
    end
  end
end

Security Sandbox

Scripts run in a sandboxed compiler. Module calls, process spawning, file access, and all system functions are blocked. Only the whitelisted actions and guards listed above are available.

Terminal Tips

Recommended fonts

  • JetBrains Mono
  • Fira Code
  • SF Mono

Terminal size

120x40 or larger for the best experience

Emoji support

Make sure your terminal supports emoji rendering — needed for the default avatar display mode