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:
POST
Path:
/notifications/watched-addresses
Body 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:
GET
Path:
/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:
GET
Path:
/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:
PATCH
Path:
/notifications/watched-addresses/:id
Body: 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:
DELETE
Path:
/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:
POST
Headers:
Content-Type: application/json
Authorization: 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_TRANSFER
orTOKEN_TRANSFER
sender
(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
WCO
TOKEN_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_count
increments on each failure.next_retry_at
is set tonow + interval
.On a successful 2xx response,
processed_at
is 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)asset
with 0x.
Receiver may be empty string
For coin transfers where
to
is 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
WCO
for 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_KEY
is 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 NULL
andnext_retry_at
in the past.
Amount precision issues
The
amount
is sent as a JSON number; for exact arithmetic, convert or handle using big number libraries on your side.
Change Log
2025-09: Added
asset
to 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