0watch API Reference

0watch exposes a REST API for account management, wallet monitoring, transaction history, anomaly alerts, webhooks, and billing. All endpoints return JSON.

Base URL: https://watch.0agent.ai

Authentication: Include your API key on all authenticated requests:

X-API-Key: owk_...

API keys are issued at signup. You can rotate them at any time via POST /api/keys/rotate.


Health

GET /api/health

Public. Check service and indexer availability.

Response

{
  "status": "ok",
  "timestamp": 1741550000000,
  "indexer": {
    "status": "ok",
    "lastBlockAt": 1741549990000,
    "latency": 1200
  }
}

status is "ok" or "degraded". No auth required.


Authentication

POST /api/auth/signup

Create a new account. Returns an API key and session token. Free tier is applied automatically; no payment required.

Request body

{
  "email": "you@example.com",
  "password": "YourPassword123",
  "first_wallet": "0xYourWalletAddress",
  "wallet_label": "my agent"
}
Field Type Required Description
email string yes Valid email address
password string yes 10–256 characters, must include uppercase, lowercase, and a digit
first_wallet string no 0x-prefixed address to add on signup
wallet_label string no Label for the first wallet

Response201 Created

{
  "accountId": "acct_abc123def456",
  "token": "<session-jwt>",
  "apiKey": "owk_...",
  "key": {
    "id": 1,
    "accountId": "acct_abc123def456",
    "ownerEmail": "you@example.com",
    "tier": "free",
    "createdAt": 1741550000000
  },
  "subscription": {
    "tier": "free",
    "status": "active"
  },
  "firstWalletAdded": false,
  "quickstart": {
    "curl": "curl -X POST ..."
  }
}

Errors

Status Condition
400 Invalid email or weak password
400 Invalid first_wallet address
409 Email already registered

POST /api/auth/login

Get a fresh session token for an existing account.

Request body

{
  "email": "you@example.com",
  "password": "YourPassword123"
}

Response200 OK

{
  "accountId": "acct_abc123def456",
  "token": "<session-jwt>",
  "subscription": { "tier": "free", "status": "active" },
  "apiKeys": { "active": 1 }
}

Errors

Status Condition
400 Missing email or password
401 Invalid credentials

API Keys

POST /api/keys

Create a new API key for the authenticated account.

Auth required. No request body.

Use either:

If you authenticate with an API key and want the new key owned by a different email, include X-Owner-Email: you@example.com.

Response201 Created

{
  "apiKey": "owk_...",
  "key": {
    "id": 2,
    "accountId": "acct_abc123def456",
    "ownerEmail": "you@example.com",
    "tier": "free",
    "createdAt": 1741550000000
  }
}

POST /api/keys/rotate

Revoke the current API key and issue a replacement. Use this to rotate credentials after a leak.

Auth required. Authenticate with the API key you want to rotate. Include X-Owner-Email: you@example.com.

Response201 Created

{
  "apiKey": "owk_...",
  "key": { "...": "..." },
  "rotation": {
    "rotatedFromKeyId": 1,
    "replacedByKeyId": 2,
    "gracePeriodEndsAt": null,
    "gracePeriodMs": 0
  }
}

Billing & Subscription

GET /api/subscription

Get current subscription details.

Auth required.

Response200 OK

{
  "accountId": "acct_abc123def456",
  "tier": "developer",
  "status": "active",
  "walletLimit": 10,
  "startedAt": 1741550000000,
  "expiresAt": 1744142000000
}

GET /api/billing/status

Get billing status for the authenticated account, including the current subscription snapshot and wallet usage.

Auth required.

Response200 OK

{
  "accountId": "acct_abc123def456",
  "subscription": {
    "tier": "free",
    "status": "active",
    "walletLimit": 3,
    "billingProvider": "none",
    "renewalMode": "none"
  },
  "usage": {
    "watchedWallets": 1,
    "walletLimit": 3
  }
}

POST /api/billing/checkout

Create a crypto checkout payment request on Base for the authenticated account.

Auth required.

Request body

{
  "tier": "developer",
  "asset": "usdc",
  "success_url": "https://yourapp.com/billing/success"
}

Valid tiers: developer, team.

Valid assets: usdc, eth.

Response201 Created

{
  "accountId": "acct_abc123def456",
  "mode": "crypto",
  "sessionId": "pr_abc123",
  "url": "https://yourapp.com/billing/success?payment_request=pr_abc123&tier=developer&asset=usdc",
  "tier": "developer",
  "asset": "usdc",
  "chainId": 8453,
  "paymentRequest": {
    "id": "pr_abc123",
    "status": "pending",
    "recipientAddress": "0x1111111111111111111111111111111111111111",
    "amountDisplay": "49.000123",
    "paymentReference": "ow_ref_123"
  },
  "plan": {
    "tier": "developer",
    "displayName": "Developer",
    "walletLimit": 10,
    "priceCents": 4900,
    "interval": "month"
  }
}

The url is a convenience redirect target that carries the generated payment_request id back to your success page. The actual payment settles on-chain to the quoted Base address.


GET /api/billing/payment-request/:id

Refresh and return the latest state for a checkout payment request.

Auth required.

Response200 OK

{
  "accountId": "acct_abc123def456",
  "paymentRequest": {
    "id": "pr_abc123",
    "status": "pending",
    "asset": "usdc",
    "chainId": 8453,
    "recipientAddress": "0x1111111111111111111111111111111111111111",
    "amountAtomic": "49000123",
    "amountDisplay": "49.000123",
    "paymentReference": "ow_ref_123",
    "txHash": null,
    "expiresAt": 1741551800000
  },
  "subscription": {
    "tier": "free",
    "status": "active",
    "billingProvider": "none"
  }
}

POST /api/billing/payment-request/:id/confirm

Confirm an on-chain payment by supplying the transaction hash that satisfied the quote.

Auth required.

Request body

{
  "payment_tx_hash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}

Response201 Created

{
  "accountId": "acct_abc123def456",
  "paymentRequest": {
    "id": "pr_abc123",
    "status": "confirmed",
    "txHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  },
  "subscription": {
    "tier": "developer",
    "status": "active",
    "billingProvider": "crypto",
    "renewalMode": "manual"
  }
}

GET /api/billing/payments

List recent checkout payment requests for the authenticated account.

Auth required.

Use ?limit=20 to bound the number of returned payment requests.

Response200 OK

{
  "accountId": "acct_abc123def456",
  "payments": [
    {
      "id": "pr_abc123",
      "status": "confirmed",
      "asset": "usdc",
      "amountDisplay": "49.000123",
      "txHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    }
  ],
  "subscription": {
    "tier": "developer",
    "status": "active",
    "billingProvider": "crypto"
  }
}

Errors

Status Condition
400 Invalid tier, asset, JSON body, or payment transaction hash
401 Missing or invalid authentication
404 Payment request not found

GET /api/usage

Get current API usage for the billing period.

Auth required.

Response200 OK

{
  "accountId": "acct_abc123def456",
  "tier": "free",
  "limit": 100,
  "remaining": 87,
  "resetAt": 1741636400000,
  "currentPeriodStart": 1741550000000,
  "currentPeriodEnd": null,
  "requests": 13
}

Wallets

GET /api/wallets

List all watched wallets for the authenticated account.

Auth required.

Response200 OK

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

POST /api/wallets

Add a wallet to the watch list. Idempotent.

Auth required.

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

Response201 Created

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

Errors

Status Condition
400 Missing or malformed address
402 Wallet limit reached for current tier

DELETE /api/wallets/:address

Remove a wallet from the watch list.

Auth required.

Response200 OK

{
  "deleted": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}

Errors

Status Condition
400 Malformed address
404 Wallet not in watch list

GET /api/wallets/:address/summary

Get a high-level summary of recent activity for a watched wallet.

Auth required.

Response200 OK

{
  "address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "label": "hot wallet",
  "recentTxCount": 12,
  "recentAlertCount": 1,
  "lastActivityAt": 1741549000000
}

Transactions

GET /api/wallets/:address/transactions

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

Auth required.

Query

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

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 — parse with BigInt()
txType string See transaction types below
decodedData object | null Decoded call data; shape 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 or invalid limit
404 Wallet not in watch list

Alerts

GET /api/wallets/:address/alerts

Fetch anomaly alerts for a watched wallet.

Auth required.

Query

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

Response200 OK

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

Anomaly types

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

Severity values: low, medium, high, critical.


GET /api/alerts/history

Fetch alert history across all wallets for the account.

Auth required.

Query

Param Type Default Description
limit integer 50 Max results
wallet string Filter by wallet address

Response200 OK

Same structure as GET /api/wallets/:address/alerts but across all wallets.


Webhooks

Webhooks fire when a transaction on a watched wallet exceeds a configured ETH threshold. Supported targets: any HTTP/HTTPS URL, and Telegram via telegram:// scheme.

GET /api/webhooks

List registered webhooks for the account.

Auth required.

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
}

POST /api/webhooks

Register a webhook for a watched wallet.

Auth required.

Request body

{
  "url": "https://example.com/hook",
  "wallet_address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "threshold_eth": 0.5
}
Field Type Required Description
url string yes Destination. Must start with http://, https://, or telegram://.
wallet_address string yes 0x-prefixed 40-character hex address
threshold_eth number yes Fire when transaction value exceeds this 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 fields
404 Wallet not in watch list

DELETE /api/webhooks/:id

Remove a webhook.

Auth required.

Response200 OK

{ "deleted": 1 }

Webhook payload

When a transaction exceeds the threshold, 0watch POSTs:

{
  "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.


Deliveries

GET /api/deliveries

Query webhook delivery history.

Auth required.

Query

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

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
}

status is "delivered" or "failed". error is null on success.


GET /api/deliveries/:id/attempts

Get all delivery attempts for a specific delivery (including retries).

Auth required.

Response200 OK

{
  "deliveryId": 1,
  "attempts": [
    {
      "attemptedAt": 1741550000000,
      "statusCode": 500,
      "error": "connection refused"
    },
    {
      "attemptedAt": 1741550005000,
      "statusCode": 200,
      "error": null
    }
  ]
}

Wallet Lookup

GET /api/lookup/:address

Public lookup for a wallet address. Returns recent activity summary without authentication.

No auth required.

Response200 OK

{
  "address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "watched": true,
  "recentTxCount": 5,
  "lastActivityAt": 1741549000000
}

Dashboard

GET /api/dashboard/stats

Account-level summary: wallet count, transaction count, alert count, recent activity.

Auth required.

Response200 OK

{
  "walletCount": 2,
  "txCount": 47,
  "alertCount": 3,
  "webhookCount": 2,
  "recentAlerts": [ ... ]
}

Waitlist

POST /api/waitlist

Add an email to the 0watch waitlist.

No auth required.

Request body

{ "email": "you@example.com" }

Response200 OK

{ "ok": true }

Rate limits

Rate limit headers on every authenticated response:

Header Description
x-ratelimit-limit Daily call limit for your tier
x-ratelimit-remaining Remaining calls today
x-ratelimit-reset Unix timestamp (seconds) when the counter resets

When exhausted, requests return 429 Too Many Requests:

{
  "error": "Rate limit exceeded for free tier",
  "tier": "free",
  "resetAt": 1741636400000
}
Tier API calls/day Wallets History
Free 100 3 7 days
Developer 10,000 10 90 days
Team 50,000 50 1 year
Enterprise 250,000 Unlimited Unlimited

Error format

All errors return:

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

Common status codes:

Status Meaning
400 Bad request — invalid input
401 Missing or invalid credentials
404 Resource not found
409 Conflict — e.g. email already registered
429 Rate limit exceeded
500 Internal server error