# Create address
Source: https://docs.wavynode.com/api-reference/endpoint/create-address
POST /projects/{projectId}/addresses
Creates a new relevant address to listen for compliance events and alerts for a given project. Requires Developer role.
Once registered, Wavy Node monitors this address and sends notifications to your webhook when it interacts with risky or blacklisted wallets.
# Create API key
Source: https://docs.wavynode.com/api-reference/endpoint/create-api-key
POST /projects/{projectId}/keys
Creates a new API key for a given project.
# Start risk analysis
Source: https://docs.wavynode.com/api-reference/endpoint/create-risk-analysis
POST /risk
Starts an asynchronous risk analysis for a wallet address on a specific chain and date range.
This endpoint starts an asynchronous analysis job. The response includes a `jobId` that you must poll to retrieve the results.
## Polling for results
After starting an analysis, poll the [Get risk analysis result](/api-reference/endpoint/get-risk-result) endpoint with the `jobId` to check for completion:
```javascript theme={null}
const jobId = "abc123...";
async function pollResult(jobId) {
while (true) {
const res = await fetch(`https://api.wavynode.com/v1/risk/${jobId}`, {
headers: { "x-api-key": "ApiKey wavy_..." }
});
const { data } = await res.json();
if (data.status === "completed") return data.result;
if (data.status === "failed") throw new Error(data.error);
await new Promise(r => setTimeout(r, 1000));
}
}
```
## Related resources
Synchronous risk check for fast single-address analysis.
Poll for the result of an async risk analysis job.
Learn how risk scores are calculated and interpreted.
# Delete address
Source: https://docs.wavynode.com/api-reference/endpoint/delete-address
DELETE /projects/{projectId}/addresses/{addressId}
Removes a relevant address from the project's monitored address list.
# Delete API key
Source: https://docs.wavynode.com/api-reference/endpoint/delete-api-key
DELETE /projects/{projectId}/keys/{keyId}
Deletes an API key for a given project.
# Get chain details
Source: https://docs.wavynode.com/api-reference/endpoint/get-chain
GET /chains/{chainId}
Retrieves detailed information about a specific blockchain network, including market data and network statistics.
# Get report download URL
Source: https://docs.wavynode.com/api-reference/endpoint/get-report-download
GET /reports/{reportId}
Retrieves a signed URL to download a specific compliance report.
`{reportId}` is the report file name (e.g. `12345.xml`), not an internal ID. Reports live in project storage under a path like `mx//2026-04/.xml` (México) or `co//2026-04/.txt` (Colombia).
Use the `projectId`, `countryCode`, and `period` query parameters so the full storage path can be reconstructed:
```bash theme={null}
curl -H "x-api-key: ApiKey wavy_your_api_key_here" \
"https://api.wavynode.com/v1/reports/12345.xml?projectId=$PROJECT_ID&countryCode=MX&period=2026-04"
```
Alternatively, pass the full storage path with the `path` query parameter instead of using `{reportId}`:
```bash theme={null}
curl -H "x-api-key: ApiKey wavy_your_api_key_here" \
"https://api.wavynode.com/v1/reports/download?path=mx/$PROJECT_ID/2026-04/12345.xml"
```
The signed URL is temporary (valid for 30 days). Use it to download the report file.
# Quick risk analysis
Source: https://docs.wavynode.com/api-reference/endpoint/get-risk-quick
GET /risk/quick
Performs a quick synchronous risk analysis on a wallet address. Returns a risk score and any detected suspicious patterns.
Use this endpoint for fast, synchronous risk checks on individual addresses. For deeper analysis with date ranges and chain filtering, use [Start risk analysis](/api-reference/endpoint/create-risk-analysis).
For an overview of how risk scores work, see [Risk scores](/concepts/risk). For detailed information about detected patterns, see [Risk analysis](/concepts/risk-analysis).
## Related resources
Learn how Wavy Node calculates risk scores and recommended actions by risk level.
Technical reference with pattern weights, severity levels, and detailed examples.
Start an async risk analysis with date range and chain filtering.
# Get risk analysis result
Source: https://docs.wavynode.com/api-reference/endpoint/get-risk-result
GET /risk/{jobId}
Polls the status and result of an asynchronous risk analysis job.
Poll this endpoint to check the status of an async risk analysis started with [Start risk analysis](/api-reference/endpoint/create-risk-analysis).
## Status values
| Status | Description |
| ----------- | --------------------------------------------------------------- |
| `pending` | Job is queued and waiting to start |
| `running` | Analysis is in progress |
| `completed` | Analysis finished successfully. `result` contains the risk data |
| `failed` | Analysis failed. `error` contains the failure reason |
## Response fields
| Field | Type | Description |
| ----------------- | ------ | --------------------------------------------------------------- |
| `address` | string | Wallet address being analyzed |
| `status` | string | Job status (pending, running, completed, failed) |
| `result` | object | Risk analysis result when status is `completed` |
| `result.risk` | number | Risk score from 0 to 100 |
| `result.reason` | string | Explanation of the risk score |
| `result.patterns` | array | Detected suspicious patterns with severity and involved wallets |
| `error` | string | Error message when status is `failed` |
| `createdAt` | number | Job creation timestamp (unix ms) |
| `updatedAt` | number | Last update timestamp (unix ms) |
## Related resources
Start a new async risk analysis job.
Synchronous risk check for fast single-address analysis.
Learn how risk scores are calculated and interpreted.
# Wallet background check
Source: https://docs.wavynode.com/api-reference/endpoint/get-wallet-background-check
GET /wallets/{wallet}/report
Generates an AI-written background check report about the compliance status and activity of a wallet address.
Runs an AI background check on a wallet address and returns a written report covering its compliance status and on-chain activity.
## Supported wallet formats
The endpoint accepts EVM, Solana, Stellar, Tron, and Bitcoin addresses:
* EVM: `0x` followed by 40 hex characters
* Solana / Tron / Stellar / Bitcoin: standard address formats of each network
## Caching
Reports are cached per address with a TTL. The endpoint returns the cached report when one exists and has not expired; otherwise it generates a fresh one.
| Query parameter | Type | Description |
| --------------- | ------- | --------------------------------------------------------------------------------- |
| `recreate` | boolean | When `true`, bypasses the cache and forces a fresh AI generation. Default `false` |
## Example
```bash theme={null}
curl -H "x-api-key: ApiKey wavy_..." \
"https://api.wavynode.com/v1/wallets/0x28C6c06298d514Db089934071355E5743bf21d60/report"
```
## Response
```json theme={null}
{
"success": true,
"data": "# Wallet Report\n\n**Address:** 0x28C6...\n**Risk level:** Low\n\n## Activity overview\n..."
}
```
The response `data` field is a markdown string, not a structured object.
# List addresses
Source: https://docs.wavynode.com/api-reference/endpoint/list-addresses
GET /projects/{projectId}/addresses
Retrieves all the relevant on-chain addresses configured for a specific project.
# List API keys
Source: https://docs.wavynode.com/api-reference/endpoint/list-api-keys
GET /projects/{projectId}/keys
Retrieves all the API keys for a given project.
# List all chains
Source: https://docs.wavynode.com/api-reference/endpoint/list-chains
GET /chains
Retrieves all the blockchain networks supported by Wavy Node.
# List compliance reports
Source: https://docs.wavynode.com/api-reference/endpoint/list-reports
GET /reports
Retrieves compliance reports filtered by project, period, and country. Reports are auto-generated documents required by regulators.
## Supported countries
| Code | Country |
| ---- | ----------- |
| `MX` | México |
| `CO` | Colombia |
| `SV` | El Salvador |
| `GT` | Guatemala |
## Query parameters
| Parameter | Type | Required | Description |
| ------------- | ------- | -------- | ----------------------------- |
| `projectId` | integer | yes | Your project ID |
| `period` | string | yes | Period in `YYYY-MM` format |
| `countryCode` | string | yes | One of `MX`, `CO`, `SV`, `GT` |
| `limit` | integer | no | Max results (default: 10) |
| `offset` | integer | no | Pagination offset |
| `search` | string | no | Filter by report name |
Reports are generated automatically based on your project's transaction activity and the regulatory requirements of each jurisdiction.
# Introduction
Source: https://docs.wavynode.com/api-reference/introduction
Compliance, fraud prevention, and risk analysis API for payment providers and crypto exchanges in Latin America.
The Wavy Node API lets you analyze blockchain addresses, monitor wallets, and manage compliance workflows for your projects.
## Base URL
```
https://api.wavynode.com/v1
```
## Authentication
All endpoints require your API key in the `x-api-key` HTTP header. The value must include the `ApiKey` prefix:
```bash highlight={1} theme={null}
curl -H "x-api-key: ApiKey wavy_your_api_key_here" \
https://api.wavynode.com/v1/chains
```
Get your API key from the [Wavy Node dashboard](https://app.wavynode.com/dashboard).
Always include the `ApiKey` prefix before your key. The correct format is:
```highlight={1} theme={null}
x-api-key: ApiKey wavy_...
```
Sending only the key without the prefix (e.g., `x-api-key: wavy_...`) will result in an authentication error.
Some API routes require you to provide the `projectId` in the URL. You can find your project's `projectId` in the dashboard under **Project Settings**.
## Response format
All responses use JSON with a consistent structure:
| Field | Type | Required | Description |
| --------- | ------- | -------- | ----------------------------------- |
| `success` | boolean | yes | Whether the request was successful |
| `data` | any | no | Response payload |
| `message` | string | no | Additional context or error message |
### Error example
```json theme={null}
{
"success": false,
"error": "invalid_request",
"message": "The api key is invalid"
}
```
## Available endpoints
Get information about supported blockchain networks.
Generate AI-written compliance reports from the wallet's risk and activity.
Analyze blockchain addresses for risk and suspicious patterns. Quick sync checks or async deep analysis.
Auto-generated regulatory compliance reports (MX, CO, SV, GT).
Manage relevant on-chain addresses for compliance monitoring.
Manage API keys for your projects programmatically.
# Regulations
Source: https://docs.wavynode.com/concepts/regulations
Regulatory framework covered by Wavy Node.
Wavy Node evaluates each transaction against the applicable regulatory frameworks in the corresponding jurisdiction.
# Mexico 🇲🇽
### LFPIORPI
*Ley Federal para la Prevención e Identificación de Operaciones con Recursos de Procedencia Ilícita.*
Under Mexican regulations, Virtual Asset Service Providers (VASPs) are classified as “Vulnerable Activity,” regulated by the "LFPIORPI (Art. 17, Section XVI):
> The regular and professional offering of virtual asset exchange by entities other than Financial Institutions carried out through electronic, digital, or similar platforms that manage or operate by facilitating or performing purchase or sale transactions of such assets owned by their customers or provide means for the custody, storage, or transfer of virtual assets other than those recognized by the Bank of Mexico under the Law Regulating Financial Technology Institutions, including transactions carried out with Mexican citizens from another jurisdiction.
Our software covers AML for Vulnerable Activities, risk matrices, establishes reporting thresholds (UMAs), and notifications to the SAT (SPPLD).
Want to know if your company needs to be regulated under this jurisdiction? Schedule a call with our team.
# Colombia 🇨🇴
### Monthly transaction reports
Under Colombian AML regulations, VASPs must report user transactions that exceed the following thresholds:
* Individual transactions of **USD 150 or more**
* Multiple transactions with a cumulative total of **USD 450 or more**
On the 1st day of each month, Wavy Node generates a **TXT report per user** with the qualifying transactions from the previous month and uploads it to the compliance reports bucket (`co///.txt`). Project admins are notified by email when the reports are ready.
# International standards 🌎
### FATF
Wavy Node implements the Financial Action Task Force recommendations:
* Recommendation 15: New technologies (virtual assets)
* Recommendation 16: Travel Rule
### Travel Rule
For virtual asset transfers exceeding USD \$1,000, Wavy Node verifies that the required originator and beneficiary information is included:
* Originator name
* Originator account or wallet number
* Originator address or national ID
* Beneficiary account or wallet number
* Beneficiary name (Coming soon)
Coming soon: support for El Salvador, Guatemala, Chile, Argentina, Brazil, USA, European Union.
# Risk scores
Source: https://docs.wavynode.com/concepts/risk
How Wavy Node calculates risk scores for blockchain addresses using deterministic pattern-based analysis.
Wavy Node assigns a risk score from 0 to 100 to each analyzed blockchain address. The scoring system is deterministic (same inputs always produce the same outputs) and pattern-based: every score is derived from the suspicious patterns detected in the address's transaction graph.
## Risk scale
| Range | Level | Recommended action |
| ------ | -------- | ------------------------------------ |
| 0-19 | Minimal | No action needed |
| 20-39 | Low | Passive monitoring |
| 40-59 | Medium | Investigation recommended |
| 60-79 | High | Priority investigation |
| 80-100 | Critical | Immediate action / regulatory report |
A score of `0` means no suspicious patterns were detected, not that the address was verified by a third party.
## How the score is calculated
The score is the weighted sum of every detected pattern, adjusted by its severity, capped at 100:
```
score = min(Σ pattern_weight × severity_multiplier, 100)
```
| Severity | Multiplier |
| ---------- | ---------- |
| `critical` | 1.2 |
| `high` | 1.0 |
| `medium` | 0.7 |
| `low` | 0.4 |
For example, a single `mixing` pattern detected as `critical` scores 72 (60 × 1.2), while an `exchange_hopping` pattern at `medium` scores 14 (20 × 0.7).
The engine analyzes the transaction graph to detect suspicious patterns like mixing, peel chains, structuring, and more. Each pattern has a base weight (e.g. `mixing` and `malicious_actor` weight 60, `peel_chain` 50, `round_tripping` 40) that is multiplied by the severity multiplier of the detected severity. See [Risk analysis](/concepts/risk-analysis) for the full weight table.
Address labels guide the detection process but never discount the score. For example, smart contracts skip `wash_trading` and `exchange_hopping` detection, and exchanges, mixers, bridges, and addresses with more than 500 interactions are treated as opaque nodes in the graph.
The system is fully deterministic: the same address and date range always produce the same result. Quick checks are cached for 1 hour and async analysis jobs for 24 hours.
## Actions by risk level
| Level | Monitoring | Investigation | Report | Block |
| ----------------- | ---------- | ------------- | -------- | -------- |
| Minimal (0-19) | Standard | No | No | No |
| Low (20-39) | Increased | Optional | No | No |
| Medium (40-59) | Intensive | Yes | Evaluate | No |
| High (60-79) | Continuous | Urgent | Yes | Evaluate |
| Critical (80-100) | Real-time | Immediate | Required | Yes |
## Reading the result
Each result includes:
* `risk` — the score from 0 to 100
* `patterns` — the detected patterns, each with `name`, `severity`, `involvedWallets`, `involvedTxs`, `usdAmount`, and a human-readable `message`
* `reason` — reserved for additional context; currently always `null`
## Suspicious activity
Escalate an address when any of the following conditions are met:
* Risk score is 70 or higher
* Any pattern with `critical` severity is detected
* Three or more patterns with `high` severity are detected
The risk scoring system is essential for compliance with regulations such as FATF recommendations, Travel Rule, Mexico's Fintech Law, and LFPIORPI.
See the full technical reference with pattern weights, severity levels, and detailed examples.
# Risk analysis
Source: https://docs.wavynode.com/concepts/risk-analysis
Detected patterns, scoring formula, and response examples from the Wavy Node risk scoring system.
This page covers the patterns Wavy Node detects and how to interpret the risk analysis results you receive from endpoints like [Quick risk analysis](/api-reference/endpoint/get-risk-quick) and [Async risk analysis](/api-reference/endpoint/create-risk-analysis). For an overview of how risk scores work, see [Risk scores](/concepts/risk).
## Detected patterns
The analysis engine identifies the following risk patterns in the transaction graph:
| Pattern | Typical severity | Description |
| ---------------------- | ---------------- | -------------------------------------------------------------------------------------- |
| `malicious_actor` | Critical | Interaction with known malicious actors, sanctioned addresses, or blacklisted entities |
| `mixing` | Critical | Use of mixing services to hide fund origins |
| `peel_chain` | High | Transaction chains to fragment and obscure funds |
| `structuring` | High | Splitting amounts to avoid reporting thresholds |
| `wash_trading` | High | Artificial transactions to simulate volume |
| `round_tripping` | High | Funds returning to origin after multiple hops |
| `mass_wallet_creation` | High | Coordinated creation of multiple wallets |
| `exchange_hopping` | Medium | Rapid movement between exchanges to break traceability |
Severity varies with the strength of the evidence. For example, `mixing` is `critical` when the address itself is a labeled mixer, `high` when it interacted with Tornado Cash, and `medium` when only small-value counterparty interactions with mixers were found. `malicious_actor` is `critical` when the address appears on the OFAC sanctions list, `high` for other blacklists, and scales up with the volume and count of malicious counterparties.
## How scores are calculated
Each detected pattern contributes points to the score. Two things determine how many points:
* **Weight** — how dangerous the pattern is by itself. For example, interacting with a known malicious actor (60) weighs far more than moving funds between exchanges (20).
* **Severity** — how strong the evidence is. `critical` means the evidence is conclusive (e.g. the address is on the OFAC sanctions list or is a labeled mixer), `high` means strong evidence (e.g. a Tornado Cash interaction), and `medium`/`low` mean indirect evidence (e.g. small-value interactions with a flagged counterparty).
The score is the sum of every pattern's points, capped at 100:
```
score = min(Σ pattern_weight × severity_multiplier, 100)
```
| Pattern | Weight |
| ---------------------- | ------ |
| `malicious_actor` | 60 |
| `mixing` | 60 |
| `peel_chain` | 50 |
| `round_tripping` | 40 |
| `wash_trading` | 35 |
| `mass_wallet_creation` | 30 |
| `structuring` | 25 |
| `exchange_hopping` | 20 |
| Severity | Multiplier |
| ---------- | ---------- |
| `critical` | 1.2 |
| `high` | 1.0 |
| `medium` | 0.7 |
| `low` | 0.4 |
### Worked examples
| Scenario | Detected patterns | Calculation | Score | Level | Action |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------- | ----- | -------- | ------------------------------------ |
| No suspicious activity found | — | no patterns to score | 0 | Minimal | No action needed |
| Small interaction with a flagged wallet (under \$100) | `mixing` `medium` | 60 × 0.7 = 42 | 42 | Medium | Investigation recommended |
| Address interacted with Tornado Cash | `mixing` `high` | 60 × 1.0 = 60 | 60 | High | Priority investigation |
| Address is a labeled mixer | `mixing` `critical` | 60 × 1.2 = 72 | 72 | High | Priority investigation |
| Labeled mixer that also splits deposits to avoid thresholds | `mixing` `critical` + `structuring` `high` | 72 + 25 = 97 | 97 | Critical | Immediate action / regulatory report |
| Multiple laundering techniques at once | `mixing` `critical` + `peel_chain` `critical` + `wash_trading` `high` + `round_tripping` `high` | 72 + 60 + 35 + 40 = 207 → capped | 100 | Critical | Immediate action / regulatory report |
The score never exceeds 100. Once several severe patterns stack up, the result is 100 — the exact value stops being informative and the response's `patterns` list becomes the main source of evidence for the report.
## Response examples
```json theme={null}
{
"success": true,
"data": {
"risk": 0,
"reason": null,
"patterns": []
}
}
```
No detected patterns. Score 0 (minimal range). Standard monitoring applies.
```json theme={null}
{
"success": true,
"data": {
"risk": 0,
"reason": null,
"patterns": []
}
}
```
A recognized exchange wallet typically has no suspicious patterns, so it returns a score of 0. Labels do not discount the score — clean behavior is what keeps the score at 0.
```json theme={null}
{
"success": true,
"data": {
"risk": 74,
"reason": null,
"patterns": [
{
"name": "mixing",
"severity": "high",
"involvedWallets": ["0xabc...", "0xdef..."],
"involvedTxs": ["0x123..."],
"message": "Address has interacted with Tornado Cash"
},
{
"name": "exchange_hopping",
"severity": "medium",
"involvedWallets": ["0xabc...", "0x789..."],
"involvedTxs": ["0x789..."],
"usdAmount": 12000,
"message": "12000 of token 0xtoken moved from 0xabc... → 0x789... within 4.2h (92% value match)"
}
]
}
}
```
Mixing interaction plus exchange hopping. Score 74 (60 + 14, high range). Requires immediate investigation.
```json theme={null}
{
"success": true,
"data": {
"risk": 97,
"reason": null,
"patterns": [
{
"name": "mixing",
"severity": "critical",
"involvedWallets": ["0xmixer...", "0xwallet1..."],
"involvedTxs": [],
"usdAmount": 150000,
"message": "Address identified as \"Tornado Cash\" — classified as mixer"
},
{
"name": "structuring",
"severity": "high",
"involvedWallets": ["0xmixer...", "0xwallet3..."],
"involvedTxs": ["0xe5f6..."],
"usdAmount": 80000,
"message": "8 deposits to 4 counterparties within 48h window, averaging $2500 each (CV: 0.12, total $20000)"
}
]
}
}
```
Mixer label combined with structuring. Score 97 (72 + 25, critical range). Urgent escalation and regulatory report required.
```json theme={null}
{
"success": true,
"data": {
"address": "0x28c6c06298d514db089934071355e5743bf21d60",
"status": "completed",
"result": {
"risk": 0,
"reason": null,
"patterns": []
},
"error": null,
"createdAt": 1754400000000,
"updatedAt": 1754400032000
}
}
```
Polling `GET /risk/{jobId}` returns the job envelope. `result` is present and populated only when `status` is `completed`; `error` is set when the job fails.
# Supported
Source: https://docs.wavynode.com/concepts/supported
Blockchain networks and tokens monitored by Wavy Node.
Wavy Node monitors multiple EVM-compatible chains and Stellar. You can also query available chains via the [List Chains](/api-reference/endpoint/list-chains) endpoint.
## Chains
| Chain | ID | Currency | Type | Explorer |
| ---------------- | ------- | -------- | ------- | ---------------------------------------------------------- |
| Ethereum Mainnet | `1` | ETH | EVM | [etherscan.io](https://etherscan.io) |
| Optimism | `10` | ETH | EVM | [optimistic.etherscan.io](https://optimistic.etherscan.io) |
| Polygon | `137` | POL | EVM | [polygonscan.com](https://polygonscan.com) |
| Lisk | `1135` | ETH | EVM | [blockscout.lisk.com](https://blockscout.lisk.com) |
| Stellar | `1234` | XLM | Stellar | [stellarchain.io](https://stellarchain.io) |
| Story Mainnet | `1514` | IP | EVM | [storyscan.io](https://www.storyscan.io) |
| Mantle | `5000` | MNT | EVM | [mantlescan.xyz](https://mantlescan.xyz) |
| Base | `8453` | ETH | EVM | [basescan.org](https://basescan.org) |
| Arbitrum | `42161` | ETH | EVM | [arbiscan.io](https://arbiscan.io) |
| Celo Mainnet | `42220` | CELO | EVM | [celoscan.io](https://celoscan.io) |
| Avalanche | `43114` | AVAX | EVM | [snowscan.xyz](https://snowscan.xyz) |
Mantle is currently inactive. Contact us if you need monitoring on this chain.
## Tokens by chain
| Token | Symbol | Contract |
| ------------------ | ------ | -------------------------------------------- |
| USD Coin | USDC | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` |
| USD Coin (Bridged) | USDC.e | `0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8` |
| Tether USD | USDT | `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9` |
| Dai Stablecoin | DAI | `0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1` |
| Wrapped Ether | WETH | `0x82aF49447D8a07e3bd95BD0d56f35241523fBab1` |
| Wrapped BTC | WBTC | `0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f` |
| PayPal USD | PYUSD | `0x46850aD61C2B7d64d08c9C754F45254596696984` |
| Token | Symbol | Contract |
| ----------- | ------ | -------------------------------------------- |
| USD Coin | USDC | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` |
| Tether USD | USDT | `0xdAC17F958D2ee523a2206206994597C13D831ec7` |
| DAI | DAI | `0x6B175474E89094C44Da98b954EedeAC495271d0F` |
| Wrapped BTC | WBTC | `0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599` |
| PayPal USD | PYUSD | `0x6c3ea9036406852006290770BEdFcAbA0e23A0e8` |
| Token | Symbol | Contract |
| -------------- | ------ | -------------------------------------------- |
| USD Coin | USDC | `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` |
| Tether USD | USDT | `0xc2132D05D31c914a87C6611C10748AEb04B58e8F` |
| Dai Stablecoin | DAI | `0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063` |
| Wrapped Ether | WETH | `0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619` |
| Wrapped SOL | WSOL | `0xd93f7E271cB87c23AaA73edC008A79646d1F9912` |
| PayPal USD | PYUSD | `0x99aF3EeA856556646C98c8B9b2548Fe815240750` |
| Token | Symbol | Contract |
| ------------- | ------ | -------------------------------------------- |
| USD Coin | USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
| Tether USD | USDT | `0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2` |
| DAI | DAI | `0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb` |
| USDS | USDS | `0x820C137fa70C8691f0e44Dc420a5e53c168921Dc` |
| Wrapped Ether | WETH | `0x4200000000000000000000000000000000000006` |
| Wrapped BTC | WBTC | `0x1ceA84203673764244E05693e42E6Ace62bE9BA5` |
| Solana | SOL | `0x311935Cd80B76769bF2ecC9D8Ab7635b2139cf82` |
| Token | Symbol | Contract |
| ------------------- | ------ | -------------------------------------------- |
| USD Coin | USDC | `0xcebA9300f2b948710d2653dD7B07f33A8B32118C` |
| Tether USD | USDT | `0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e` |
| Celo Colombian Peso | cCOP | `0x8A567e2aE79CA692Bd748aB832081C45de4041eA` |
| Wrapped Ether | WETH | `0xD221812de1BD094f35587EE8E174B07B6167D9Af` |
| Wrapped BTC | WBTC | `0x8aC2901Dd8A1F17a1A4768A6bA4C3751e3995B2D` |
| Token | Symbol | Contract |
| -------------- | ------ | -------------------------------------------- |
| USD Coin | USDC | `0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E` |
| Tether USD | USDT | `0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7` |
| Dai Stablecoin | DAI.e | `0xd586E7F844cEa2F87f50152665BCbc2C279D8d70` |
| Wrapped Ether | WETH.e | `0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB` |
| Wrapped BTC | WBTC | `0x0555E30da8f98308EdB960aa94C0Db47230d2B9c` |
| Token | Symbol | Contract |
| ------------- | ------ | -------------------------------------------- |
| Bridged USDC | USDC.e | `0xF242275d3a6527d877f2c927a82D9b057609cc71` |
| Tether USD | USDT | `0x05D032ac25d322df992303dCa074EE7392C117b9` |
| Wrapped Ether | WETH | `0x4200000000000000000000000000000000000006` |
| Lisk | LSK | `0xac485391EB2d7D88253a7F1eF18C37f4242D1A24` |
| Wrapped BTC | WBTC | `0x03C7054BCB39f7b2e5B2c7AcB37583e32D70Cfa3` |
| Token | Symbol | Contract |
| -------------- | ------ | -------------------------------------------- |
| USD Coin | USDC | `0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9` |
| Tether USD | USDT | `0x201EBa5CC46D216Ce6DC03F6a759e8E766e956aE` |
| Wrapped Ether | WETH | `0xdEAddEaDdeadDEadDEADDEAddEADDEAddead1111` |
| Wrapped Mantle | WMNT | `0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8` |
| Token | Symbol | Contract |
| --------------------- | ------ | -------------------------------------------- |
| Stargate Bridged USDC | USDC.e | `0xF1815bd50389c46847f0Bda824eC8da914045D14` |
| Stargate Bridged WETH | WETH | `0xBAb93B7ad7fE8692A878B95a8e689423437cc500` |
Need a chain or token that's not listed here? [Contact us](https://wavynode.com) and we can evaluate adding it.
# Transactions
Source: https://docs.wavynode.com/concepts/transactions
How Wavy Node monitors and analyzes on-chain transactions in real time
Wavy Node analyzes blockchain transactions in real time to detect suspicious patterns, fraud, and money laundering. Each analyzed address receives a risk score and a set of detected patterns.
## How it works
Add the relevant on-chain addresses for your project using the [Addresses API](/api-reference/endpoint/list-addresses). These are the wallets you want to monitor for compliance.
Submit addresses for analysis using the [Risk analysis](/api-reference/endpoint/get-risk-quick) endpoints. The system traverses the transaction graph to detect suspicious patterns.
Each address receives a risk score (0-100), a risk level, and a list of detected patterns with their severity.
Based on the results, you can approve, investigate, or escalate. Wavy Node sends real-time alerts to your [integration webhook](/integrations/endpoints) when suspicious activity is detected.
## Detected patterns
When analyzing the transaction graph, Wavy Node can identify the following suspicious patterns:
| Pattern | Typical severity | Description |
| ---------------------- | ---------------- | -------------------------------------------------------------------------------------- |
| `malicious_actor` | Critical | Interaction with known malicious actors, sanctioned addresses, or blacklisted entities |
| `mixing` | Critical | Use of mixing services to hide fund origins |
| `peel_chain` | High | Transaction chains to fragment and obscure funds |
| `structuring` | High | Splitting amounts to avoid reporting thresholds |
| `wash_trading` | High | Artificial transactions to simulate volume |
| `round_tripping` | High | Funds returning to origin after multiple hops |
| `mass_wallet_creation` | High | Coordinated creation of multiple wallets |
| `exchange_hopping` | Medium | Rapid movement between exchanges to break traceability |
Severity varies with the strength of the evidence. See [Risk analysis](/concepts/risk-analysis) for the full detection rules and scoring formula.
## Risk levels
| Level | Score | Recommended action |
| -------- | ------ | ------------------------------------ |
| Minimal | 0-19 | No action needed |
| Low | 20-39 | Passive monitoring |
| Medium | 40-59 | Investigation recommended |
| High | 60-79 | Priority investigation |
| Critical | 80-100 | Immediate action / regulatory report |
## Response example
```json theme={null}
{
"success": true,
"data": {
"risk": 56,
"reason": null,
"patterns": [
{
"name": "malicious_actor",
"severity": "medium",
"involvedWallets": ["0xabc...", "0xdef..."],
"involvedTxs": ["0x123..."],
"usdAmount": 80,
"message": "Interacted with 1 known malicious actors ($80.00 USD)"
},
{
"name": "exchange_hopping",
"severity": "medium",
"involvedWallets": ["0xabc...", "0xdef..."],
"involvedTxs": ["0x456..."],
"usdAmount": 12000,
"message": "12000 of token 0xtoken moved from 0xabc... → 0xdef... within 3.1h (88% value match)"
}
]
}
}
```
Here the score is the weighted sum: `malicious_actor` (60 × 0.7 = 42) plus `exchange_hopping` (20 × 0.7 = 14), for a total of 56 (medium range).
Addresses with a `risk` score of 80-100 (critical range) should be reported to the regulator per applicable regulations. See [Regulations](/concepts/regulations) for details.
# Welcome to Wavy Node
Source: https://docs.wavynode.com/index
Enterprise-grade automated compliance, regulatory monitoring and reporting for stablecoin payments.
## The best shield for your company!
At Wavy Node, we protect companies that are leading the stablecoin-based financial future, reducing the risk of fines for regulatory non-compliance.
We **simplify compliance through a fully integrated system** that includes:
* On-chain screening and risk scoring in real-time.
* Cross-checking against wallet and user blacklists.
* Automatic generation of background review using AI.
* Creation of reporting formats that Virtual Asset Service Providers (VASPs) require to submit to their regulator.
Our software **reduces the need for multiple vendors** and is perfect for infrastructure companies in the payments ecosystem: payment processors, remittances, exchanges, P2P solutions, international payment processors, and payment gateways.
## We're ready to take care of you!
Ready to integrate into your system easily and quick, with the best industry standards.
Real-time transaction and user monitoring, risk scoring, alert sending, and review generation.
We manage your risk internationally, with information on lists and regulations. Not only on-chain screening.
We automatically create the report formats that you have to submit to your regulator.
## Use cases
With real-time behavioral algorithms and a risk matrix based on international standards, we review your users' activity and assign risk weights to protect your business.
We alert you to suspicious transactions and generate a review of wallet behavior for your compliance team, saving time for decision-making.
Create in-depth investigations of your users' activity in a simple way to strengthen your folders.
We monitor regulatory requirements and automate the creation of regulatory report formats that you have to submit to your regulators on a regular basis, without complications.
## Quickstart
Integrate Wavy Node in minutes, and protect your company today!
Set up your API key and create a complete analysis of your ecosystem.
***
Schedule a call and our support team will help you step by step to protect your company.
Check our real-time system status and uptime monitoring.
# Authentication
Source: https://docs.wavynode.com/integrations/authentication
Learn how to verify that incoming requests to your integration are authentically from Wavy Node using HMAC-SHA256.
Wavy Node uses HMAC-SHA256 to sign every request it sends to your integration. This guarantees that requests are legitimate and haven't been tampered with in transit.
Think of it as a seal of authenticity: only someone with the shared `SECRET` can generate a valid signature.
## How it works
```mermaid theme={null}
sequenceDiagram
participant WN as Wavy Node
participant YS as Your Server
WN->>WN: 1. Build canonical message
WN->>WN: 2. Sign with HMAC-SHA256 using SECRET
WN->>YS: 3. Send request + headers (x-wavynode-hmac, x-wavynode-timestamp)
YS->>YS: 4. Rebuild canonical message
YS->>YS: 5. Recalculate signature with SECRET
alt Signatures match
YS-->>WN: 200 OK ✅
else Signatures don't match
YS-->>WN: 401 Unauthorized ❌
end
```
### Step by step
1. Wavy Node builds a canonical message from: HTTP method, path, body, and timestamp
2. Signs that message with HMAC-SHA256 using the shared SECRET
3. Sends the request with two extra headers:
* `x-wavynode-hmac` — the generated signature
* `x-wavynode-timestamp` — the timestamp in milliseconds
4. Your server receives the request
5. Recalculates the signature using the same SECRET and request data
6. If signatures match → the request is legitimate (200). If not → reject it (401)
## The shared SECRET
The SECRET is a 32-character hex string that both sides know:
```
615d9b7ea991acfa33f823c374c3a062
```
It must be configured in two places:
| Location | How to configure |
| -------------------- | ----------------------------------------------- |
| Wavy Node dashboard | **Settings → Integración → Integration Secret** |
| Your server (`.env`) | `SECRET=615d9b7ea991acfa33f823c374c3a062` |
Both values must be exactly the same. If they differ, signatures won't match and all requests will fail with 401.
### Generate a new SECRET
```bash theme={null}
openssl rand -hex 16
```
## Authentication headers
Every request from Wavy Node includes these headers:
| Header | Type | Description |
| ---------------------- | ------ | ------------------------------------------------------------- |
| `x-wavynode-hmac` | string | Base64 encoded HMAC-SHA256 signature of the canonical message |
| `x-wavynode-timestamp` | string | Timestamp in milliseconds (epoch) |
## Canonical message
The signature is not calculated on the raw request, but on a standardized canonical message. This ensures both sides build exactly the same string before signing.
The canonical message is formed by concatenating these values separated by `::`:
* The uppercase HTTP method (`GET`, `POST`, etc.)
* The lowercase request path (e.g., `/webhook`)
* The stringified request body with keys sorted alphabetically, or `{}` if no body
* The timestamp from the `x-wavynode-timestamp` header
```
GET::/users/123::{}::1757050233763
```
## Using `@wavynode/utils`
The easiest way to verify requests is with the `validateSignature` function:
```typescript theme={null}
import { validateSignature } from '@wavynode/utils';
const isValid = validateSignature({
method: 'POST',
path: '/webhook',
body: req.body,
timestamp: parseInt(req.headers['x-wavynode-timestamp']),
secret: process.env.SECRET,
timeTolerance: 300000,
signature: req.headers['x-wavynode-hmac']
});
if (!isValid) {
// The request is not from Wavy Node
// or has been tampered with.
// Reject the request.
}
```
| Parameter | Type | Description |
| --------------- | ------ | -------------------------------------------------------------------------------- |
| `method` | string | The HTTP method of the request |
| `path` | string | The path of the request |
| `body` | object | The request body |
| `timestamp` | number | The timestamp from the `x-wavynode-timestamp` header |
| `secret` | string | Your integration's secret from **Settings → Integración** in the dashboard |
| `timeTolerance` | number | Time tolerance in milliseconds to prevent replay attacks (recommended: `300000`) |
| `signature` | string | The signature from the `x-wavynode-hmac` header |
You can also build the canonical message manually with `formCanonicalMessage`:
```typescript theme={null}
import { formCanonicalMessage } from "@wavynode/utils";
const canonicalMessage = formCanonicalMessage({
method: "GET",
path: "/users/user-123",
body: {},
timestamp: 1712764800000
});
```
## Manual authentication
If you are not using the `@wavynode/utils` package, you can implement the authentication logic yourself.
Concatenate the values separated by `::` as described above:
```
GET::/users/123::{}::1757050233763
```
Create a `sha256` HMAC of the canonical string using your integration's secret as the key. Base64 encode the result.
```javascript theme={null}
import crypto from 'node:crypto';
const createHmacSignature = (message, secret) => {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(message);
return hmac.digest('base64');
};
```
Compare the signature you created with the one from the `x-wavynode-hmac` header. If they match, the request is authentic.
## Why is HMAC necessary?
Without HMAC authentication, anyone who knows your integration URL could:
* Send fake webhooks simulating transactions that never happened
* Query user data without authorization
* Inject false notifications into your system
HMAC guarantees that only Wavy Node can communicate with your integration server.
# Endpoints
Source: https://docs.wavynode.com/integrations/endpoints
Implement the two required routes in your integration server to receive user data requests and real-time notifications from Wavy Node.
Your integration is a **single server** (one URL) that exposes **two routes**. When you configure the integration URL in the Wavy Node dashboard (e.g., `https://your-server.com`), Wavy Node appends the route paths automatically:
* `GET {integrationUrl}/users/{foreign_user_id}`
* `POST {integrationUrl}/webhook`
You only need **one server** and **one integration URL**. Do not create separate servers or URLs for each route. Wavy Node sends requests to both routes on the same base URL.
## `GET /users/{foreign_user_id}`
Wavy Node calls this endpoint to retrieve information about a specific user. The `{foreign_user_id}` parameter is the user's ID in your system (the same ID you provided when registering the wallet address).
The `foreign_user_id` is the identifier you send when creating an address via `POST /projects/{projectId}/addresses`. This is how Wavy Node links wallets to users in your system.
Your endpoint should return a JSON object with the following structure:
```json theme={null}
{
"givenName": "Maria Guadalupe",
"maternalSurname": "Sánchez",
"paternalSurname": "Rodríguez",
"birthdate": "1992-05-15",
"nationality": "MX",
"phoneNumber": {
"countryCode": "+52",
"phoneNumber": 5512345678
},
"email": "maria.guadalupe@example.com",
"address": {
"country": "MX",
"region": "CDMX",
"city": "Ciudad de México",
"street": "Avenida Insurgentes Sur",
"colonia": "Condesa",
"exteriorNumber": "123",
"interiorNumber": "4B",
"postalCode": "06100"
},
"mexico": {
"rfc": "ROSM920515XXX",
"curp": "ROSM920515MDFRXXXX",
"actividadEconomica": 612012,
"cuentaRelacionada": "1234567890",
"monedaCuentaRelacionada": 1,
"documentoIdentificacion": {
"tipoIdentificacion": 1,
"numeroIdentificacion": "IDMEX12345678"
}
}
}
```
| Field | Type | Description |
| ------------------------- | ------ | ---------------------------------------------------------- |
| `givenName` | string | The individual's given name(s) |
| `maternalSurname` | string | Maternal surname |
| `paternalSurname` | string | Paternal surname |
| `birthdate` | string | Date of birth in `YYYY-MM-DD` format (ISO 8601) |
| `nationality` | string | ISO 3166-1 alpha-2 country code |
| `phoneNumber.countryCode` | string | Phone country code |
| `phoneNumber.phoneNumber` | number | Phone number |
| `email` | string | Email address |
| `address` | object | Physical address |
| `mexico` | object | Fields required for Mexican legislation reports (optional) |
The `mexico` object is only needed if you have Mexican compliance enabled in your dashboard.
### Person types for Mexican reports
When Mexican legislation is active, the `mexico` data feeds the AVI report. A report supports three mutually exclusive person types, and exactly one applies per user.
An individual account holder. At least one of `rfc`, `curp`, or `birthdate` is required.
```json theme={null}
{
"givenName": "Maria Guadalupe",
"paternalSurname": "García",
"maternalSurname": "López",
"birthdate": "1990-05-15",
"nationality": "MX",
"mexico": {
"rfc": "GALM900515XXX",
"curp": "GALM900515MDFRPN09",
"actividadEconomica": 8140001,
"cuentaRelacionada": "012180001234567890",
"monedaCuentaRelacionada": 1,
"documentoIdentificacion": {
"tipoIdentificacion": 1,
"numeroIdentificacion": "INE123456789"
}
}
}
```
A legal entity. At least one of `rfc` or `fechaConstitucion` is required, and a legal representative (`representanteApoderado`) is mandatory.
```json theme={null}
{
"tipoPersona": "moral",
"denominacionRazon": "Empresa Ejemplo SA de CV",
"fechaConstitucion": "2015-03-20",
"nationality": "MX",
"representanteApoderado": {
"givenName": "Juan Carlos",
"paternalSurname": "Martínez",
"maternalSurname": "Ruiz",
"birthdate": "1985-08-10",
"rfc": "MARJ850810XXX",
"curp": "MARJ850810HDFRRL09",
"documentoIdentificacion": {
"tipoIdentificacion": 1,
"numeroIdentificacion": "INE987654321"
}
},
"mexico": {
"rfc": "EEJ150320XXX",
"giroMercantil": 5612001,
"actividadEconomica": 5612001,
"cuentaRelacionada": "014320001234567890",
"monedaCuentaRelacionada": 1,
"documentoIdentificacion": {
"tipoIdentificacion": 4,
"numeroIdentificacion": "ACTA-CONST-2015"
}
}
}
```
A trust. At least one of `rfc` or `identificadorFideicomiso` is required, and a fiduciary delegate (`delegadoFiduciario`) is mandatory.
```json theme={null}
{
"tipoPersona": "fideicomiso",
"denominacionRazon": "Fiduciario Ejemplo SA",
"nationality": "MX",
"delegadoFiduciario": {
"givenName": "Ana María",
"paternalSurname": "Torres",
"maternalSurname": "Vega",
"birthdate": "1980-02-25",
"rfc": "TOVA800225XXX",
"curp": "TOVA800225MDFRRN09",
"documentoIdentificacion": {
"tipoIdentificacion": 1,
"numeroIdentificacion": "INE555444333"
}
},
"mexico": {
"rfc": "FEJ800101XXX",
"identificadorFideicomiso": "FID-2020-00123",
"cuentaRelacionada": "012180009876543210",
"monedaCuentaRelacionada": 1,
"documentoIdentificacion": {
"tipoIdentificacion": 4,
"numeroIdentificacion": "CONTRATO-FID-2020"
}
}
}
```
Codes such as `tipoIdentificacion` (1=INE, 2=Passport, 4=Incorporation deed), `actividadEconomica`, and `giroMercantil` follow the SAT catalogs, published at [sppld.sat.gob.mx](https://sppld.sat.gob.mx/pld/interiores/activos.html).
## `POST /webhook`
Wavy Node sends real-time notifications to this endpoint. The request body contains:
| Field | Type | Description |
| ------ | ---------------- | ----------------------------------------- |
| `type` | string | `notification` or `error` |
| `data` | object or string | The notification payload or error message |
### Notification payload
When `type` is `notification`, the `data` object contains transaction and compliance details:
```json theme={null}
{
"type": "notification",
"data": {
"id": 1,
"projectId": 1,
"chainId": 42161,
"address": {
"id": 543,
"userId": "user-in-your-db-123",
"address": "0xyour-address-involved",
"description": "Your address' description"
},
"txHash": "some-tx-hash",
"timestamp": "2025-08-20T05:10:57.228Z",
"amount": {
"value": 1000000000000000000,
"usd": 3000
},
"token": {
"name": "Ethereum",
"symbol": "ETH",
"decimals": 18,
"address": null
},
"inflictedLaws": [
{
"name": "The name of the law inflicted",
"description": "Description of the law",
"source": "Source of the law",
"risk": "warn",
"country": "mexico",
"countryCode": "MX"
}
]
}
}
```
| Field | Type | Description |
| ----------------------------- | ------ | ---------------------------------------------------------------------------------------- |
| `id` | number | Notification ID |
| `projectId` | number | Project ID |
| `chainId` | number | Chain ID where the transaction occurred |
| `address.id` | number | Address ID |
| `address.userId` | string | User ID in your system (the `foreign_user_id` you provided when registering the address) |
| `address.address` | string | Wallet address |
| `address.description` | string | Address description |
| `txHash` | string | Transaction hash |
| `timestamp` | string | Transaction timestamp (ISO 8601) |
| `amount.value` | number | Amount in the token's smallest unit |
| `amount.usd` | number | Amount in USD |
| `token.name` | string | Token name |
| `token.symbol` | string | Token symbol |
| `token.decimals` | number | Token decimals |
| `token.address` | string | Token contract address (`null` for native tokens) |
| `inflictedLaws` | array | Laws inflicted by the transaction |
| `inflictedLaws[].name` | string | Law name |
| `inflictedLaws[].description` | string | Law description |
| `inflictedLaws[].source` | string | Law source |
| `inflictedLaws[].risk` | string | `warn` or `illegal` |
| `inflictedLaws[].country` | string | Country name |
| `inflictedLaws[].countryCode` | string | ISO country code |
### Error payload
When `type` is `error`, the `data` field is a string containing the error message.
# Overview
Source: https://docs.wavynode.com/integrations/overview
Connect your application with Wavy Node by setting up a single integration server with two routes to receive real-time compliance notifications and serve user data.
Integrations connect your application with Wavy Node. By creating an integration, you set up a single server that Wavy Node communicates with to receive real-time notifications about your users' on-chain activity and to retrieve the user data needed for compliance.
## How it works
Your integration is a **single server** that exposes **two routes** under the same base URL. When you configure the integration URL in the dashboard (e.g., `https://your-server.com`), Wavy Node calls both routes on that same server:
1. `GET {integrationUrl}/users/{foreign_user_id}` — Wavy Node requests user data from your system for compliance checks and report generation.
2. `POST {integrationUrl}/webhook` — Wavy Node sends you real-time alerts when suspicious activity is detected.
You only need **one server** and **one integration URL**. Do not create separate servers or separate URLs for each route — both routes live on the same server.
```mermaid theme={null}
sequenceDiagram
participant You as Your App
participant WN as Wavy Node
participant BC as Blockchain
You->>WN: 1. Register addresses with foreign_user_id
WN->>BC: 2. Monitor on-chain activity
BC-->>WN: Transaction detected
WN-->>You: 3. POST /webhook (real-time alert)
WN->>You: 4. GET /users/{foreign_user_id}
You-->>WN: User data for compliance
WN->>WN: 5. Generate compliance report
```
### Wallet-to-user matching
The match between wallets and users happens when you register addresses in our system. When you create an address via the API, you include the `foreign_user_id`, which is the user's ID in your system. This is how we know which user owns each wallet.
If a user has multiple wallets, register each wallet with the same `foreign_user_id`. We handle the linking automatically.
When we need user data (for example, to generate a compliance report), we call your integration endpoint `/users/{foreign_user_id}`. You must return the required user data for each legislation you have enabled in your project.
For example, if you have Mexican legislation active, all users must include the `mexico` field populated with the user's personal and fiscal information:
```json theme={null}
{
"givenName": "Juan",
"paternalSurname": "Pérez",
"maternalSurname": "López",
"nationality": "MX",
"mexico": {
"rfc": "PELJ900101XXX",
"curp": "PELJ900101HDFRPN09",
"actividadEconomica": 1234567,
"documentoIdentificacion": {
"tipoIdentificacion": 1,
"numeroIdentificacion": "ABC123456"
}
}
}
```
See [Endpoints](/integrations/endpoints) for the full response schema. The complete type definitions for each legislation are available in the `@wavynode/utils` package.
Your endpoint must return the required data for all active legislations in your project. Missing or empty fields will cause compliance report generation to fail.
| Active Legislation | Required Field | Content |
| ------------------ | -------------- | ------------------------------------------------ |
| Mexico (MX) | `mexico` | Personal data, RFC/CURP, address, ID document |
| Colombia (CO) | `colombia` | User data required by Colombian AML regulations |
| El Salvador (SV) | `elSalvador` | User data required by Salvadoran AML regulations |
| Guatemala (GT) | `guatemala` | User data required by Guatemalan AML regulations |
### Integration flow
Use `POST /projects/{projectId}/addresses` to add wallet addresses you want to monitor. Include the `foreign_user_id` parameter to link each wallet to a user in your system.
Wavy Node watches your registered addresses for suspicious transactions, regulatory violations, and interactions with blacklisted wallets.
When Wavy Node needs user information (e.g., to generate a compliance report), it calls your `GET /users/{foreign_user_id}` endpoint. You must return the required user data for all the legislations you have enabled in your project. See the type definitions in `@wavynode/utils` for the exact fields required by each legislation.
When suspicious activity is detected, Wavy Node sends a notification to your `POST /webhook` endpoint with the transaction details and regulatory violations.
All requests from Wavy Node are signed with HMAC-SHA256 so you can verify their authenticity.
### Organization compliance data
Before Wavy Node can generate regulatory reports, you must configure your organization's compliance data in the dashboard. Navigate to **Management → Organization** in the sidebar and fill in the **Compliance Data** section. This includes:
* **Platform domain** — Your platform's domain (auto-formatted to uppercase, e.g., `wavynode.com` → `WAVYNODE`).
* **Tax IDs** — The tax identifier for each country where you have active legislation:
This information is required to generate regulatory reports. Without it, reports will not be generated.
| Country | Field | Example |
| ---------------- | ----- | ------------------- |
| Mexico (MX) | RFC | `XAXX010101000` |
| Colombia (CO) | NIT | `900123456-7` |
| El Salvador (SV) | NIT | `0614-123456-001-0` |
| Guatemala (GT) | NIT | `1234567-8` |
## Getting started
There are two ways to create a new integration:
A ready-to-go Nitro application with all required endpoints and authentication already set up. Ideal for local development.
Use any framework or language. Start by setting up authentication.
## `@wavynode/utils` package
We provide an npm package with utilities to simplify the integration process:
```bash theme={null}
npm install @wavynode/utils
```
This package includes:
* `validateSignature` — Verify the authenticity of requests from Wavy Node.
* Type definitions for each legislation's required user data (e.g., `MexicoUserData`, `ColombiaUserData`).
## Next steps
Learn how to verify requests from Wavy Node.
Implement the required endpoints for your integration.
Explore the full API documentation.
# Setup with template
Source: https://docs.wavynode.com/integrations/setup
Step-by-step guide to set up a local development environment for your Wavy Node integration using the official template.
This guide walks you through setting up a local development environment using the [official template](https://github.com/wavy-node/integration). By the end, you'll have a running server that receives real-time compliance alerts and serves user data to Wavy Node.
This setup is intended for local development and testing. For production deployments, replace ngrok with your own publicly accessible server and use environment-specific secrets.
## Prerequisites
* [Node.js](https://nodejs.org/) or [Bun](https://bun.sh/) installed
* [ngrok](https://ngrok.com/) installed (to expose your local server)
* A [Wavy Node](https://app.wavynode.com/dashboard) account with a project created
* An API key from your project
## Organization compliance data
Before anything else, configure your organization's compliance data in the dashboard. Navigate to **Management → Organization** in the sidebar and fill in the **Compliance Data** section:
1. **Platform domain** — Your platform's domain. It's auto-formatted to uppercase (e.g., `wavynode.com` → `WAVYNODE`).
2. **Tax IDs** — The tax identifier for each country where you have active legislation:
This information is required to generate regulatory reports. Without it, reports will not be generated.
| Country | Field | Example |
| ---------------- | ----- | ------------------- |
| Mexico (MX) | RFC | `XAXX010101000` |
| Colombia (CO) | NIT | `900123456-7` |
| El Salvador (SV) | NIT | `0614-123456-001-0` |
| Guatemala (GT) | NIT | `1234567-8` |
## Step 1: Clone and install
```bash theme={null}
git clone https://github.com/wavy-node/integration
cd integration
bun install
```
You can also use `npm install` if you prefer Node.js over Bun.
## Step 2: Configure environment variables
Copy the template and edit:
```bash theme={null}
cp .env.template .env
```
Your `.env` file needs these values:
```bash .env highlight={2} theme={null}
SECRET=your_32_character_hex_secret
WAVYNODE_API_KEY=wavy_your_api_key_here
PROJECT_ID=1
PORT=3001
```
### Generate the `SECRET`
The `SECRET` is a 32-character hex string used to sign requests between Wavy Node and your integration. Generate one with:
```bash theme={null}
openssl rand -hex 16
```
This outputs something like `615d9b7ea991acfa33f823c374c3a062`. Save this value — you'll also need to configure it in the Wavy Node dashboard.
The `SECRET` in your `.env` must match the `integration_secret` configured in your Wavy Node project. If they don't match, all requests will fail signature verification.
### Get your API key
Find your API key in the Wavy Node dashboard under your project's **API Keys** section. The format is `ApiKey wavy_...`.
When making API requests, the `x-api-key` header must include the `ApiKey` prefix:
```highlight={1} theme={null}
x-api-key: ApiKey wavy_your_api_key_here
```
Sending only the key without the prefix will result in an authentication error.
## Step 3: Implement your routes
The template includes two routes on the same server that you need to customize:
### `GET /users/{userId}`
Wavy Node calls this endpoint to get user data for compliance reports. Return a JSON object with the user's information:
```typescript theme={null}
// Return user data from your database
{
givenName: "Maria Guadalupe",
maternalSurname: "Sánchez",
paternalSurname: "Rodríguez",
birthdate: "1992-05-15",
nationality: "MX",
phoneNumber: {
countryCode: "+52",
phoneNumber: 5512345678
},
email: "maria.guadalupe@example.com",
address: {
country: "MX",
region: "CDMX",
city: "Ciudad de México",
street: "Avenida Insurgentes Sur",
colonia: "Condesa",
exteriorNumber: "123",
interiorNumber: "4B",
postalCode: "06100"
},
mexico: {
rfc: "ROSM920515XXX",
curp: "ROSM920515MDFRXXXX",
actividadEconomica: 612012,
cuentaRelacionada: "1234567890",
monedaCuentaRelacionada: 1,
documentoIdentificacion: {
tipoIdentificacion: 1,
numeroIdentificacion: "IDMEX12345678"
}
}
}
```
The `mexico` object is required only if you have Mexican legislation active in your project. See [Endpoints](/integrations/endpoints) for the full response schema.
### `POST /webhook`
Wavy Node sends real-time notifications when suspicious activity is detected. The template already handles signature verification — you just need to add your business logic:
```typescript theme={null}
// Notification payload
{
type: "notification",
data: {
id: 1,
projectId: 1,
chainId: 42161,
address: { userId: "user-123", address: "0x..." },
txHash: "0x...",
amount: { value: 1000000000000000000, usd: 3000 },
inflictedLaws: [{ name: "...", risk: "warn", countryCode: "MX" }]
}
}
```
## Step 4: Start the server
```bash theme={null}
bun run dev
```
The server runs at `http://localhost:3001`.
## Step 5: Expose with ngrok
In a separate terminal:
```bash theme={null}
ngrok http 3001
```
This generates a public URL like `https://abc123.ngrok-free.dev`.
```mermaid theme={null}
flowchart LR
WN[Wavy Node] -->|HTTPS| NG[ngrok URL]
NG -->|tunnel| LS[localhost:3001]
```
## Step 6: Configure your project in Wavy Node
Update your project with the integration URL and secret. In the dashboard, go to **Settings → Integración** and fill in:
* **Integration URL** — Your ngrok public URL (e.g., `https://abc123.ngrok-free.dev`). This is the single base URL for your server — Wavy Node appends `/users/{id}` and `/webhook` automatically.
* **Integration Secret** — The same 32-character secret from your `.env` file
Click **Save changes** when done.
Do not include route paths in the integration URL. Use `https://abc123.ngrok-free.dev`, not `https://abc123.ngrok-free.dev/webhook`.
The `integration_secret` must be exactly the same as the `SECRET` in your `.env` file.
## Step 7: Register addresses
Register the wallet addresses you want to monitor, linking them to users in your system:
```bash theme={null}
curl -X POST "https://api.wavynode.com/v1/projects/$PROJECT_ID/addresses" \
-H "Content-Type: application/json" \
-H "x-api-key: ApiKey wavy_your_api_key_here" \
-d '{
"address": "0x2c63bbbcc4b40ca0f149717195fea66a8f1a0395",
"description": "Main wallet",
"foreign_user_id": "user-123"
}'
```
The `foreign_user_id` is the user's ID in your system. When Wavy Node needs data for this user, it calls `GET /users/user-123` on your integration.
If the address already exists in the project, the `foreign_user_id` and `description` are updated instead of creating a duplicate.
## Step 8: Test the integration
The template includes a test script that simulates the calls Wavy Node makes to your server. With the server running and ngrok active:
```bash theme={null}
bun run test-integration.ts
```
The script runs three tests:
1. `GET /users/user-123` — verifies your user data endpoint responds correctly with a valid HMAC signature
2. `POST /webhook` — sends a simulated notification with a sample transaction and inflicted laws
3. Invalid signature — sends a request with a fake signature to verify your middleware rejects it with 401
```bash theme={null}
=== WavyNode Integration Test ===
Server: http://localhost:3001
Secret: 615d9b7e...
--- Testing GET /users/user-123 ---
Status: 200 OK
✅ GET /users passed
--- Testing POST /webhook ---
Status: 200 OK
✅ POST /webhook passed
--- Testing invalid signature (should fail with 401) ---
Status: 401 Unauthorized
✅ Invalid signature correctly rejected
=== Results: 3 passed, 0 failed ===
```
The third test shows ✅ even though the server returned 401. This is a negative test — it verifies your server correctly rejects unauthorized requests. If it returned 200 instead, that would mean your server accepts any request, which is a security issue.
## Compliance reports
Once your integration is running, compliance reports are generated automatically. Here's how the report lifecycle works:
```mermaid theme={null}
flowchart TD
A[Wavy Node monitors registered addresses] --> B[Transactions detected and stored with USD value]
B --> C{Monthly cron - 1st of each month}
C --> D[Group transactions by user]
D --> E{User total > 210 UMAs?}
E -->|Yes| F[GET /users/foreign_user_id]
F --> G[Generate compliance XML]
G --> H[Upload report to storage]
H --> I[Notify project admins by email]
E -->|No| J[No report needed]
```
For Mexican legislation, reports are generated when a user's monthly transaction total exceeds 210 UMAs (\~\$24,635 MXN). The threshold is updated automatically based on the current UMA value.
Query your reports via the API:
```bash theme={null}
curl -H "x-api-key: ApiKey wavy_your_api_key_here" \
"https://api.wavynode.com/v1/reports?projectId=$PROJECT_ID&period=2026-04&countryCode=MX"
```
## Next steps
Understand how HMAC signature verification works.
Full reference for the user data and webhook endpoints.
# Quickstart
Source: https://docs.wavynode.com/quickstart
Integrate Wavy Node into your platform in minutes
This guide gets you from zero to monitoring wallet addresses and receiving compliance alerts. By the end, you'll have registered an address, run a risk scan, and know how to access your compliance reports.
## Prerequisites
* A [Wavy Node](https://app.wavynode.com/dashboard) account
* Your API key (available in the dashboard)
* A project with a `projectId` (found in **Project Settings**)
## Step 1: Set up your credentials
Sign up on the Wavy Node dashboard and copy your API key from **Project Settings**.
```bash highlight={1} theme={null}
export WAVYNODE_API_KEY="wavy_your_api_key_here"
export PROJECT_ID="your_project_id"
```
The `x-api-key` header must include the `ApiKey` prefix followed by your key. The correct format is:
```highlight={1} theme={null}
x-api-key: ApiKey wavy_your_api_key_here
```
A common mistake is sending only the key without the prefix. Requests without `ApiKey` will be rejected with an authentication error.
## Step 2: Check supported chains
Verify which blockchain networks are available:
```bash theme={null}
curl -H "x-api-key: ApiKey wavy_your_api_key_here" \
https://api.wavynode.com/v1/chains
```
```json theme={null}
{
"success": true,
"data": [
{
"id": 42161,
"name": "Arbitrum",
"rpc_url": "https://arb1.arbitrum.io/rpc",
"active": true,
"explorer_url": "https://arbiscan.io",
"currency_symbol": "ETH",
"currency_decimals": 18
}
]
}
```
## Step 3: Register an address for monitoring
Add a wallet address to your project so Wavy Node can monitor it:
```bash theme={null}
curl -X POST "https://api.wavynode.com/v1/projects/$PROJECT_ID/addresses" \
-H "Content-Type: application/json" \
-H "x-api-key: ApiKey wavy_your_api_key_here" \
-d '{
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"description": "Main treasury wallet",
"foreign_user_id": "user-001"
}'
```
The `foreign_user_id` links this wallet to a user in your system. When you set up an [integration](/integrations/overview), Wavy Node uses this ID to request user data for compliance reports.
## Step 4: Run a risk scan
Perform a quick risk analysis on the registered address:
```bash theme={null}
curl -H "x-api-key: ApiKey wavy_your_api_key_here" \
"https://api.wavynode.com/v1/risk/quick?address=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
```
```json theme={null}
{
"success": true,
"data": {
"risk": 0,
"reason": null,
"patterns": []
}
}
```
When suspicious patterns are detected, the response includes them with their severity and a human-readable message:
```json theme={null}
{
"success": true,
"data": {
"risk": 60,
"reason": null,
"patterns": [
{
"name": "mixing",
"severity": "critical",
"involvedWallets": ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"],
"involvedTxs": [],
"message": "Address identified as mixer"
}
]
}
}
```
A `risk` score under 20 indicates minimal risk. Addresses scoring above 60 should be investigated further. For deeper analysis with date ranges and chain filtering, use the [async risk analysis](/api-reference/endpoint/create-risk-analysis) endpoint.
## Step 5: Access compliance reports
Compliance reports are generated automatically based on your project's activity and active legislations. Download them from the Wavy Node dashboard under **Reports > Regulatory Thresholds**.
Reports are available for México (MX) and Colombia (CO). Support for El Salvador (SV) and Guatemala (GT) is coming soon.
## What's next
You've registered an address and run your first risk scan. To receive real-time alerts and enable automatic compliance report generation, set up an integration:
```mermaid theme={null}
flowchart LR
A[✅ Quickstart] --> B[Set up Wavy Node integration]
B --> C[Scan risk on addresses]
B --> D[Receive real-time alerts]
B --> E[Auto-generate compliance reports]
```
Connect your app to receive webhooks and serve user data for compliance.
Understand how risk scores are calculated.
Explore the regulations Wavy Node covers.
Browse all available endpoints.