0watch API Reference

0watch exposes a REST API for managing watched wallets, querying transaction history, retrieving anomaly alerts, and configuring webhook notifications. All endpoints return JSON.

Base URL: http://localhost:3001 (default, configurable via API_PORT env var)

No authentication required — the API is designed for local or internal deployment. Restrict network access at the infrastructure level.


Health

GET /api/health

Check service availability.

Response

{
  "status": "ok",
  "timestamp": 1741550000000
}

timestamp is Unix time in milliseconds.


Wallets

GET /api/wallets

List all watched wallets.

Response

{
  "wallets": [
    {
      "address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "label": "hot wallet"
    }
  ]
}

Addresses are returned in lowercase.


POST /api/wallets

Add a wallet to the watch list. Idempotent — adding an existing wallet succeeds without creating a duplicate.

Request body

{
  "address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "label": "hot wallet"
}
Field Type Required Description
address string yes 0x-prefixed 40-character hex address
label string no Human-readable name. Defaults to "".

Response201 Created

{
  "address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "label": "hot wallet"
}

Errors

Status Condition
400 Missing or malformed address
400 Invalid JSON body

DELETE /api/wallets/:address

Remove a wallet from the watch list.

Parameters

Param Description
address 0x-prefixed 40-character hex address

Response200 OK

{
  "deleted": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}

Errors

Status Condition
400 Malformed address
404 Wallet not in watch list

Transactions

GET /api/wallets/:address/transactions

Fetch transaction history for a watched wallet. Returns transactions where the wallet is either sender or recipient.

Parameters

Param Description
address 0x-prefixed 40-character hex address

Query

Param Type Default Range Description
limit integer 100 1–1000 Max results to return

Response200 OK

{
  "address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "count": 1,
  "transactions": [
    {
      "hash": "0xdeadbeef...",
      "blockNumber": 1000,
      "timestamp": 1700000000,
      "fromAddress": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "toAddress": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
      "valueWei": "1000000000000000000",
      "txType": "eth_transfer",
      "decodedData": null,
      "gasUsed": 21000,
      "status": 1,
      "chainId": 8453
    }
  ]
}

Transaction fields

Field Type Description
hash string Transaction hash
blockNumber integer Block number where confirmed
timestamp integer Unix timestamp (seconds)
fromAddress string Sender address (lowercase)
toAddress string Recipient address (lowercase)
valueWei string Value transferred in wei (string — parse with BigInt())
txType string See transaction types below
decodedData object | null Decoded call data — structure depends on txType
gasUsed integer Gas consumed
status integer 1 = success, 0 = reverted
chainId integer EVM chain ID (8453 = Base)

Transaction types

txType Description decodedData shape
eth_transfer Native ETH send { to, value, tokenAddress?, tokenSymbol? }
erc20_transfer ERC20 transfer(address, uint256) { to, value, tokenAddress, tokenSymbol? }
erc20_approval ERC20 approve(address, uint256) { spender, value, tokenAddress }
uniswap_swap Uniswap V2/V3/Universal Router swap { router, inputToken?, outputToken?, amountIn?, amountOut? }
unknown Any other calldata null

Errors

Status Condition
400 Malformed address
400 limit out of range or non-numeric
404 Wallet not in watch list

Alerts

GET /api/wallets/:address/alerts

Fetch anomaly alerts for a watched wallet. Alerts are generated by the indexer when unusual activity is detected.

Parameters

Param Description
address 0x-prefixed 40-character hex address

Query

Param Type Default Range Description
limit integer 50 1–500 Max results to return

Response200 OK

{
  "address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "count": 1,
  "alerts": [
    {
      "walletAddress": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "txHash": "0xdeadbeef...",
      "anomalyType": "large_transfer",
      "severity": "high",
      "details": {
        "valueWei": "50000000000000000000",
        "threshold": "10000000000000000000"
      },
      "detectedAt": 1741550000000
    }
  ]
}

Alert fields

Field Type Description
walletAddress string The monitored wallet
txHash string Transaction that triggered the alert
anomalyType string Type of anomaly detected (see below)
severity string low, medium, high, or critical
details object Anomaly-specific metadata
detectedAt integer Unix timestamp in milliseconds

Anomaly types

Type Description
failed_tx Transaction reverted on-chain
large_transfer Transfer value exceeds threshold
high_velocity Unusually high transaction frequency

Errors

Status Condition
400 Malformed address
400 limit out of range or non-numeric
404 Wallet not in watch list

Webhooks

Webhooks fire when a high-value transaction is detected on a watched wallet. 0watch POSTs a JSON payload to your endpoint. Telegram is also supported natively.

GET /api/webhooks

List registered webhooks.

Query

Param Description
wallet Optional. Filter by wallet address.

Response200 OK

{
  "webhooks": [
    {
      "id": 1,
      "url": "https://example.com/hook",
      "walletAddress": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "thresholdEth": 0.5,
      "createdAt": 1741550000000
    }
  ],
  "count": 1
}

Errors

Status Condition
400 Malformed wallet address
404 Wallet not in watch list

POST /api/webhooks

Register a webhook for a watched wallet.

Request body

{
  "url": "https://example.com/hook",
  "wallet_address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "threshold_eth": 0.5
}
Field Type Required Description
url string yes Destination URL. Must start with http://, https://, or telegram://.
wallet_address string yes 0x-prefixed 40-character hex address.
threshold_eth number yes Fire when a transaction value exceeds this amount in ETH.

Response201 Created

{
  "id": 1,
  "url": "https://example.com/hook",
  "walletAddress": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "thresholdEth": 0.5,
  "createdAt": 1741550000000
}

Errors

Status Condition
400 Invalid or missing url
400 Invalid or missing wallet_address
400 Invalid or missing threshold_eth (must be a positive number)
400 Invalid JSON body

DELETE /api/webhooks/:id

Remove a webhook by ID.

Parameters

Param Description
id Webhook ID (integer)

Response200 OK

{
  "deleted": 1
}

Errors

Status Condition
400 Invalid webhook ID
404 Webhook not found

Webhook payload

When a transaction exceeds the registered threshold, 0watch POSTs this payload:

{
  "event": "high_value_transaction",
  "walletAddress": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "thresholdEth": 0.5,
  "valueEth": 1.25,
  "transaction": {
    "hash": "0xdeadbeef...",
    "blockNumber": 27480000,
    "timestamp": 1741550000,
    "fromAddress": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "toAddress": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
    "valueWei": "1250000000000000000",
    "txType": "eth_transfer",
    "status": 1,
    "chainId": 8453
  }
}

Your endpoint must respond with any 2xx status. On failure, 0watch retries once after 5 seconds. Failed deliveries are logged and queryable via GET /api/deliveries.


Deliveries

GET /api/deliveries

Query webhook delivery history.

Query

Param Type Default Range Description
wallet string Optional. Filter by wallet address.
limit integer 25 1–200 Max results to return

Response200 OK

{
  "deliveries": [
    {
      "id": 1,
      "webhookId": 1,
      "webhookUrl": "https://example.com/hook",
      "walletAddress": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "txHash": "0xdeadbeef...",
      "event": "high_value_transaction",
      "status": "delivered",
      "error": null,
      "deliveredAt": 1741550000000
    }
  ],
  "count": 1
}
Field Description
status "delivered" or "failed"
error Error message on failure, null on success
deliveredAt Unix timestamp in milliseconds

Error Format

All errors return a JSON body:

{
  "error": "Description of what went wrong."
}

Running the API

# Default — SQLite at data/0watch.db, port 3001
npm run api

# Custom config
DB_PATH=/path/to/db API_PORT=8080 npm run api

The API starts the indexer database and begins serving immediately. No migrations needed — the schema is applied automatically on first run.