# 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 normalized
  * `watch_coin` (boolean, optional, default `true`): Watch native coin (WCO) transfers
  * `watch_token` (boolean, optional, default `true`): Watch ERC-20 token transfers
  * `webhook_url` (string, required): HTTPS URL to receive webhooks

Example:

```bash
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):

```json
{
  "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:

```bash
curl -X GET https://oracle.w-chain.com/api/notifications/watched-addresses \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Success response (200):

```json
{
  "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:

```bash
curl -X GET https://oracle.w-chain.com/api/notifications/watched-addresses/ID_HERE \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Success response (200):

```json
{
  "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:

```bash
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):

```json
{
  "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:

```bash
curl -X DELETE https://oracle.w-chain.com/api/notifications/watched-addresses/ID_HERE \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Success response (200):

```json
{ "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 ID
* `watched_addresses_id` (string, UUID): The watched\_addresses row ID
* `watcher_id` (number): Your watcher ID
* `hash` (string): 0x-prefixed transaction hash
* `type` (string enum): `COIN_TRANSFER` or `TOKEN_TRANSFER`
* `sender` (string): 0x-prefixed sender address
* `receiver` (string): 0x-prefixed receiver address. For contract creations or transactions without a `to`, 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:

```json
{
  "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 to `now + 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)

```ts
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` and `next_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 to `WCO`; 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).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.w-chain.com/advanced-features/b2b-address-notification-service.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
