Quickstart
Integrate in 5 minutes
Authenticate with an API key, watch an address, receive and verify a signed webhook, then spin up a non-custodial HD payment session.
- An ArgusPulse account (SaaS or self-hosted instance)
- An API key from the dashboard (Settings → API Keys)
- Your preferred HTTP client or one of the SDKs below
Authenticate with an API key
Pass your key in the X-API-Key header on every request.
# Verify connectivity, returns feature flags and edition info curl https://api.arguspulse.io/api/v1/meta \ -H "X-API-Key: YOUR_API_KEY"
import { ArgusPulseClient } from '@arguspulse/sdk'; const client = new ArgusPulseClient({ baseUrl: 'https://api.arguspulse.io', apiKey: process.env.ARGUS_API_KEY!, }); // Or use a Keycloak bearer token for user-facing flows: // token: async () => await getFreshAccessToken()
from arguspulse import ArgusPulseClient import os client = ArgusPulseClient( base_url="https://api.arguspulse.io", api_key=os.environ["ARGUS_API_KEY"], ) # Or use a bearer token: # token=lambda: refresh_keycloak_token()
use ArgusPulse\Client; $client = new Client([ 'baseUrl' => 'https://api.arguspulse.io', 'apiKey' => $_ENV['ARGUS_API_KEY'], ]); // Or a bearer token (auto-refresh callable also supported): // 'token' => fn() => $keycloakClient->getFreshToken()
Watch an address
Register any on-chain address to start receiving deposit and withdrawal events.
curl -X POST https://api.arguspulse.io/api/v1/wallets/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "network": "BSC", "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "label": "Hot wallet", "direction": "IncomingOnly" }' # direction: "Both" | "IncomingOnly" | "OutgoingOnly" # network: BSC | ETH | TRON | SOL | BTC | BASE | ARBITRUM | POLYGON | OPTIMISM …
await client.createWallet({ network: 'BSC', address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', label: 'Hot wallet', direction: 'IncomingOnly', // 'Both' | 'IncomingOnly' | 'OutgoingOnly' });
from arguspulse import CreateWalletInput client.create_wallet(CreateWalletInput( network="BSC", address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", label="Hot wallet", direction="IncomingOnly", ))
$wallet = $client->createWallet([ 'network' => 'BSC', 'address' => '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 'label' => 'Hot wallet', 'direction' => 'IncomingOnly', ]);
Receive & verify a webhook
Every delivery is signed. Always verify, never trust unsigned payloads.
Headers on every delivery: X-ArgusPulse-Signature: sha256=<hex> X-ArgusPulse-Timestamp: <unix seconds> X-ArgusPulse-Event-Id: <uuid> Verification: hex = lowercase( HMAC_SHA256( secret, "${timestamp}.${rawBody}" ) ) Reject if: computed != header OR |now - timestamp| > 300s (replay guard)
Always verify against the raw request body bytes, not a re-serialised object.
# 1. Register a webhook endpoint curl -X POST https://api.arguspulse.io/api/v1/webhooks/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.example/hooks/arguspulse", "secret": "whsec_your_random_32_char_secret", "network": "BSC", "deliveryMode": 0 }' # 2. Verify an incoming webhook (bash + openssl) # RAW_BODY and TIMESTAMP come from the request SIG=$(echo -n "${TIMESTAMP}.${RAW_BODY}" | openssl dgst -sha256 -hmac "${SECRET}" -hex | sed 's/^.* //') echo "sha256=${SIG}" # compare to X-ArgusPulse-Signature header
import express from 'express'; import { parseVerifiedWebhook, type WebhookPayload } from '@arguspulse/sdk'; const app = express(); // IMPORTANT: capture the raw body, do NOT use express.json() on this route app.post('/hooks/arguspulse', express.raw({ type: 'application/json' }), (req, res) => { try { const event = parseVerifiedWebhook<WebhookPayload>({ secret: process.env.ARGUS_WEBHOOK_SECRET!, rawBody: req.body.toString('utf8'), signatureHeader: req.header('x-arguspulse-signature'), timestampHeader: req.header('x-arguspulse-timestamp'), }); console.log(`Deposit ${event.amountDecimal} ${event.symbol} on ${event.network}`); res.sendStatus(200); } catch { res.sendStatus(401); // invalid signature or stale timestamp } });
from flask import Flask, request, abort from arguspulse import parse_verified_webhook app = Flask(__name__) @app.post("/hooks/arguspulse") def webhook(): # Get raw body BEFORE any JSON parsing raw_body = request.get_data(as_text=True) try: event = parse_verified_webhook( secret=app.config["ARGUS_WEBHOOK_SECRET"], raw_body=raw_body, signature_header=request.headers.get("X-ArgusPulse-Signature"), timestamp_header=request.headers.get("X-ArgusPulse-Timestamp"), ) except ValueError: abort(401) print(f"Deposit {event['amountDecimal']} {event['symbol']} on {event['network']}") return "", 200 # FastAPI variant, see SDK README for async example
<?php require 'vendor/autoload.php'; use ArgusPulse\ArgusPulseException; use ArgusPulse\Webhook; // Always read raw body BEFORE json_decode $rawBody = file_get_contents('php://input'); $signatureHeader = $_SERVER['HTTP_X_ARGUSPULSE_SIGNATURE'] ?? null; $timestampHeader = $_SERVER['HTTP_X_ARGUSPULSE_TIMESTAMP'] ?? null; try { $event = Webhook::parseVerifiedPayload( secret: $_ENV['ARGUS_WEBHOOK_SECRET'], rawBody: $rawBody, signatureHeader: $signatureHeader, timestampHeader: $timestampHeader, toleranceSeconds: 300, // replay-attack window ); error_log("Deposit {$event->amountDecimal} {$event->symbol} on {$event->network}"); http_response_code(200); } catch (ArgusPulseException $e) { http_response_code(401); // invalid signature or stale timestamp } // Laravel: see SDK README for the controller + CSRF exclusion pattern
Confirmed deposit payload
{
"eventId": "evt_01hwxyz…",
"network": "BSC",
"txHash": "0xabc…",
"address": "0xd8dA…",
"counterparty": "0x742d…",
"direction": "Incoming",
"symbol": "USDT",
"amount": "1000000000000000000", // base units
"amountDecimal": "1",
"valueUsd": "1.00",
"valueTry": "32.50",
"status": "Confirmed",
"confirmations": 15
}Create an HD payment session
Non-custodial: configure your xpub once, then derive a unique address per order.
Provide your BIP32 extended public key (xpub/ypub/zpub) once per network. ArgusPulse derives a fresh deposit address for every payment session via BIP32. Your private key never leaves your systems, so ArgusPulse cannot sign transactions or move funds.
# Step 1, configure your xpub once per network (public key only) curl -X PUT https://api.arguspulse.io/api/v1/payments/config \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "network": "BSC", "xpub": "xpub6CUGRUon…" }' # Step 2, create a payment session per order curl -X POST https://api.arguspulse.io/api/v1/payments/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "network": "BSC", "asset": "USDT", "expectedAmount": "1000000000000000000", "reference": "order-12345", "callbackUrl": "https://your-server.example/paid" }' # Response includes "address", the unique BIP32-derived deposit address # Status: Pending → Detected → Confirmed (or Expired / Cancelled)
// One-time xpub setup per network await client.setPaymentXpub('BSC', 'xpub6CUGRUon…'); // Create a session per order const session = await client.createPayment({ network: 'BSC', asset: 'USDT', expectedAmount: '1000000000000000000', // base units (optional) reference: 'order-12345', // idempotent callbackUrl: 'https://your-server.example/paid', }); console.log('Pay to:', session.address); // unique BIP32-derived address
from arguspulse import CreatePaymentInput # One-time xpub setup client.set_payment_xpub("BSC", "xpub6CUGRUon…") # Create a session per order session = client.create_payment(CreatePaymentInput( network="BSC", asset="USDT", expected_amount="1000000000000000000", reference="order-12345", callback_url="https://your-server.example/paid", )) print("Pay to:", session.address)
// One-time xpub setup $client->setPaymentXpub('BSC', 'xpub6CUGRUon…'); // Create a session per order $session = $client->createPayment([ 'network' => 'BSC', 'asset' => 'USDT', 'expectedAmount' => '1000000000000000000', 'reference' => 'order-12345', 'callbackUrl' => 'https://your-server.example/paid', ]); echo 'Pay to: ' . $session->address; // unique BIP32-derived address
Next steps
Browse every endpoint, try live requests, inspect schemas.
System status →Check PostgreSQL, Redis, RabbitMQ, and Elasticsearch health.
Use the SDK's streamDetections() async generator for live deposit feeds without polling.
Every counterparty is auto-screened. Manage alerts via GET /api/v1/aml/alerts or the SDK.