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.

Prerequisites
  • 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
1

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()
2

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',
]);
3

Receive & verify a webhook

Every delivery is signed. Always verify, never trust unsigned payloads.

Signature scheme
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
}
4

Create an HD payment session

Non-custodial: configure your xpub once, then derive a unique address per order.

How it works

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

Interactive API reference →

Browse every endpoint, try live requests, inspect schemas.

System status →

Check PostgreSQL, Redis, RabbitMQ, and Elasticsearch health.