B2B Address Notification Service
Coin & Token Transfer Watcher
This document explains how to use the Watched Addresses system to receive real-time webhook notifications for W Chain coin (WCO) transfers and ERC-20 token transfers involving your registered addresses.
The system provides:
A CRUD API to manage watched addresses and webhook URLs
Real-time POST webhooks on transfers matching your watch rules
Automatic retries with exponential backoff on delivery failures
All user-facing addresses and hashes in API responses and webhooks are 0x-prefixed. Internally, addresses are stored lowercase without the 0x prefix.
Authentication
All endpoints require an API key via the HTTP Authorization header:
Authorization: Bearer YOUR_API_KEY
If the key is valid, the server resolves your watcher_id and authorizes the request. If invalid or missing, the server returns 401 Unauthorized.
Base Path
Base URL: https://oracle.w-chain.com/api
Address Normalization
Input (create/update): You may send addresses with or without the 0x prefix. The server normalizes by stripping 0x if present and storing lowercase hex without 0x.
Output (list/get/update responses and webhooks): All addresses and transaction hashes are 0x-prefixed for consistency with wallets and explorers.
Endpoints
Create a watched address
Method:
POSTPath:
/notifications/watched-addressesBody fields:
address(string, required): Address with or without 0x; must be 40 hex characters when normalizedwatch_coin(boolean, optional, defaulttrue): Watch native coin (WCO) transferswatch_token(boolean, optional, defaulttrue): Watch ERC-20 token transferswebhook_url(string, required): HTTPS URL to receive webhooks
Example:
curl -X POST https://oracle.w-chain.com/api/notifications/watched-addresses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"address": "0xabc1234567890abcdefabc1234567890abcdef0",
"watch_coin": true,
"watch_token": true,
"webhook_url": "https://yourapp.example.com/watched-address-webhook"
}'Success response (200):
{
"success": true,
"data": {
"id": "...",
"watcher_id": 123,
"address": "0xabc1234567890abcdefabc1234567890abcdef0",
"watch_coin": true,
"watch_token": true,
"webhook_url": "https://yourapp.example.com/watched-address-webhook",
"created_at": "...",
"updated_at": "..."
}
}List watched addresses
Method:
GETPath:
/notifications/watched-addresses
Example:
curl -X GET https://oracle.w-chain.com/api/notifications/watched-addresses \
-H "Authorization: Bearer YOUR_API_KEY"Success response (200):
{
"success": true,
"data": [
{
"id": "...",
"watcher_id": 123,
"address": "0xabc1234567890abcdefabc1234567890abcdef0",
"watch_coin": true,
"watch_token": true,
"webhook_url": "https://yourapp.example.com/watched-address-webhook",
"created_at": "...",
"updated_at": "..."
}
]
}Get a watched address by ID
Method:
GETPath:
/notifications/watched-addresses/:id
Example:
curl -X GET https://oracle.w-chain.com/api/notifications/watched-addresses/ID_HERE \
-H "Authorization: Bearer YOUR_API_KEY"Success response (200):
{
"success": true,
"data": {
"id": "...",
"watcher_id": 123,
"address": "0xabc1234567890abcdefabc1234567890abcdef0",
"watch_coin": true,
"watch_token": true,
"webhook_url": "https://yourapp.example.com/watched-address-webhook",
"created_at": "...",
"updated_at": "..."
}
}Update a watched address
Method:
PATCHPath:
/notifications/watched-addresses/:idBody: Any subset of the create fields (
address,watch_coin,watch_token,webhook_url)
Example:
curl -X PATCH https://oracle.w-chain.com/api/notifications/watched-addresses/ID_HERE \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "watch_token": false }'Success response (200):
{
"success": true,
"data": {
"id": "...",
"watcher_id": 123,
"address": "0xabc1234567890abcdefabc1234567890abcdef0",
"watch_coin": true,
"watch_token": false,
"webhook_url": "https://yourapp.example.com/watched-address-webhook",
"created_at": "...",
"updated_at": "..."
}
}Delete a watched address
Method:
DELETEPath:
/notifications/watched-addresses/:id
Example:
curl -X DELETE https://oracle.w-chain.com/api/notifications/watched-addresses/ID_HERE \
-H "Authorization: Bearer YOUR_API_KEY"Success response (200):
{ "success": true }Webhook Delivery
When a transfer involving any of your watched addresses occurs, we send a POST request with JSON to the webhook_url configured for that watched address.
Method:
POSTHeaders:
Content-Type: application/jsonAuthorization: Bearer <api_key>this is the same API Key used by the Watcher identity, should be used to ensure the authenticity of the webhook request
Timeout: 10 seconds. If your endpoint does not respond within 10 seconds, it is treated as a failure and retried.
Payload fields
These fields are sent in the request body. Values are formatted for user-facing consumption as described.
id(number): Internal notification IDwatched_addresses_id(string, UUID): The watched_addresses row IDwatcher_id(number): Your watcher IDhash(string): 0x-prefixed transaction hashtype(string enum):COIN_TRANSFERorTOKEN_TRANSFERsender(string): 0x-prefixed sender addressreceiver(string): 0x-prefixed receiver address. For contract creations or transactions without ato, this may be an empty string.amount(number | null):COIN_TRANSFER: Amount is in wei, as a JSON number. Note: very large values may exceed JavaScript’s exact integer range; handle carefully if you require exact arithmetic.
TOKEN_TRANSFER: Raw token units as a JSON number (decimals not applied). Fetch the token’s decimals and normalize on your side if needed.
asset(string):COIN_TRANSFER: The literal string
WCOTOKEN_TRANSFER: 0x-prefixed token contract address
timestamp(string): Timestamp of notification row creation as produced by Postgres (timestamp without time zone). Treat as a textual timestamp; convert/timezone as desired on your side.webhook_url(string): The target URL (provided for your reference)api_key(string | null): The API key stored for your watcher, if any (also sent via the Authorization header)next_retry_at(string | null): Scheduled time for the next retry (if previously failed), else null
Example payload:
{
"id": 12345,
"watched_addresses_id": "a1b2c3d4-1111-2222-3333-abcdefabcdef",
"watcher_id": 678,
"hash": "0x3fa9e1...c1d2",
"type": "TOKEN_TRANSFER",
"sender": "0x1111111111111111111111111111111111111111",
"receiver": "0x2222222222222222222222222222222222222222",
"amount": 2500000000000000000,
"asset": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
"timestamp": "2025-09-10 08:00:00.000000",
"webhook_url": "https://yourapp.example.com/watched-address-webhook",
"api_key": "YOUR_API_KEY_OR_NULL",
"next_retry_at": null
}Retry Policy (Exponential Backoff)
If a webhook delivery fails (non-2xx response, timeout, or network error), the system schedules a retry using the following backoff (minutes):
1, 3, 10, 15, 60
On the Nth failure, the Nth interval is used; after the 5th failure, retries continue every 60 minutes.
retry_countincrements on each failure.next_retry_atis set tonow + interval.On a successful 2xx response,
processed_atis set and the notification will not be retried further.
The retry worker selects unprocessed notifications whose next_retry_at is due.
Implementation Notes
Address formatting
Inputs accept with or without 0x; stored lowercase without 0x internally.
API responses (create/list/get/update) return addresses with 0x.
Webhook payload prefixes
hash,sender,receiver, and (for tokens)assetwith 0x.
Receiver may be empty string
For coin transfers where
tois null (e.g., contract creation), receiver is emitted as an empty string.
Amount type
Currently sent as a JSON number. If exact precision is required in your system, convert or handle carefully to avoid precision loss in JavaScript environments.
Asset field
WCOfor native coin transfers; otherwise the token contract address (0x-prefixed) for token transfers.
Timeouts
Webhook POSTs time out in 10 seconds (treated as failure).
Authorization header on webhook
If an API key is present for the watcher, the webhook request to your server includes
Authorization: Bearer <api_key>.
Example Receiver (Pseudocode)
import express from 'express';
const app = express();
app.use(express.json());
app.post('/watched-address-webhook', (req, res) => {
// Optionally verify Authorization header matches what you expect
// const auth = req.headers['authorization'];
const n = req.body; // See payload fields above
// Handle idempotency if you store notifications (e.g., by `n.id`)
if (n.type === 'COIN_TRANSFER') {
// n.asset === 'WCO'
// n.amount is wei (number); beware precision in JS
} else if (n.type === 'TOKEN_TRANSFER') {
// n.asset is token contract address (0x-prefixed)
// n.amount is raw units; fetch token decimals to normalize
}
res.status(200).send('ok');
});
app.listen(3000);Troubleshooting
401 Unauthorized on API calls
Ensure
Authorization: Bearer YOUR_API_KEYis present and valid.
Webhook not received
Confirm your server is reachable over HTTPS and returns 200 within 10 seconds.
Check if retries are scheduled by inspecting your logs or querying notifications with
processed_at IS NULLandnext_retry_atin the past.
Amount precision issues
The
amountis sent as a JSON number; for exact arithmetic, convert or handle using big number libraries on your side.
Change Log
2025-09: Added
assetto notification schema and payload. For coin transfers set toWCO; for token transfers set to the token contract address.2025-09: Standardized 0x-prefix formatting in API responses and webhook payloads (hash, sender, receiver, token asset).
Last updated