Skip to main content
Balance monitors allow you to track balance thresholds and trigger webhooks when conditions are met. This is essential for low balance alerts, fraud detection, and automated workflows.

Understanding balance monitors

A balance monitor consists of:
  • Balance ID: The balance to monitor
  • Condition: The trigger rule (field, operator, value)
  • Callback URL: Where to send webhook notifications
  • Description: Human-readable explanation

Creating a basic monitor

Set up a low balance alert:
1

Create balance monitor

POST /balance-monitors
{
  "balance_id": "bln_customer_wallet",
  "description": "Alert when balance drops below $100",
  "condition": {
    "field": "balance",
    "operator": "<",
    "value": 100.00,
    "precision": 100
  }
}
2

Response

{
  "monitor_id": "mon_abc123xyz",
  "balance_id": "bln_customer_wallet",
  "description": "Alert when balance drops below $100",
  "condition": {
    "field": "balance",
    "operator": "<",
    "value": 100.00,
    "precision": 100,
    "precise_value": "10000"
  },
  "created_at": "2024-01-15T10:30:00Z"
}
3

Webhook notification when triggered

When the balance drops below $100, you’ll receive:
{
  "event": "balance.monitor",
  "data": {
    "monitor_id": "mon_abc123xyz",
    "balance_id": "bln_customer_wallet",
    "description": "Alert when balance drops below $100",
    "condition": {
      "field": "balance",
      "operator": "<",
      "value": 100.00
    }
  }
}

Monitoring different fields

You can monitor various balance fields:

Available balance

POST /balance-monitors
{
  "balance_id": "bln_wallet_001",
  "description": "Low available balance",
  "condition": {
    "field": "balance",
    "operator": "<",
    "value": 50.00,
    "precision": 100
  }
}

Credit balance

POST /balance-monitors
{
  "balance_id": "bln_wallet_001",
  "description": "High credit activity",
  "condition": {
    "field": "credit_balance",
    "operator": ">",
    "value": 10000.00,
    "precision": 100
  }
}

Debit balance

POST /balance-monitors
{
  "balance_id": "bln_wallet_001",
  "description": "Unusual spending",
  "condition": {
    "field": "debit_balance",
    "operator": ">",
    "value": 5000.00,
    "precision": 100
  }
}

Inflight balance

POST /balance-monitors
{
  "balance_id": "bln_merchant_wallet",
  "description": "High pending transactions",
  "condition": {
    "field": "inflight_balance",
    "operator": ">",
    "value": 2000.00,
    "precision": 100
  }
}

Supported operators

Less than

< - Triggers when field value is less than threshold

Less than or equal

<= - Triggers when field value is less than or equal to threshold

Greater than

> - Triggers when field value exceeds threshold

Greater than or equal

>= - Triggers when field value is greater than or equal to threshold

Equal

= - Triggers when field value exactly matches threshold

Not equal

!= - Triggers when field value differs from threshold

Common use cases

Low balance warning

POST /balance-monitors
{
  "balance_id": "bln_customer_wallet",
  "description": "Notify customer to top up wallet",
  "condition": {
    "field": "balance",
    "operator": "<",
    "value": 25.00,
    "precision": 100
  }
}
Use case: Send email/SMS to customer when their balance is running low.

Fraud detection

POST /balance-monitors
{
  "balance_id": "bln_user_wallet",
  "description": "Suspicious high debit activity",
  "condition": {
    "field": "debit_balance",
    "operator": ">",
    "value": 1000.00,
    "precision": 100
  }
}
Use case: Alert security team when daily spending exceeds normal patterns.

Merchant payout threshold

POST /balance-monitors
{
  "balance_id": "bln_merchant_earnings",
  "description": "Ready for payout",
  "condition": {
    "field": "balance",
    "operator": ">=",
    "value": 100.00,
    "precision": 100
  }
}
Use case: Automatically trigger payouts when merchant earnings reach minimum threshold.

Reserve requirement

POST /balance-monitors
{
  "balance_id": "bln_lending_pool",
  "description": "Below required reserve",
  "condition": {
    "field": "balance",
    "operator": "<",
    "value": 50000.00,
    "precision": 100
  }
}
Use case: Alert when liquidity pool needs replenishment.

Dormant account detection

POST /balance-monitors
{
  "balance_id": "bln_user_account",
  "description": "Account with zero balance",
  "condition": {
    "field": "balance",
    "operator": "=",
    "value": 0.00,
    "precision": 100
  }
}
Use case: Identify and flag dormant accounts.

Managing monitors

List all monitors

GET /balance-monitors
[
  {
    "monitor_id": "mon_abc123",
    "balance_id": "bln_wallet_001",
    "description": "Low balance alert",
    "condition": {
      "field": "balance",
      "operator": "<",
      "value": 100.00
    },
    "created_at": "2024-01-15T10:30:00Z"
  },
  {
    "monitor_id": "mon_xyz789",
    "balance_id": "bln_wallet_001",
    "description": "High credit activity",
    "condition": {
      "field": "credit_balance",
      "operator": ">",
      "value": 10000.00
    },
    "created_at": "2024-01-15T11:00:00Z"
  }
]

Get monitors for specific balance

GET /balance-monitors/balance/bln_wallet_001

Get specific monitor

GET /balance-monitors/mon_abc123

Update a monitor

PUT /balance-monitors/mon_abc123
{
  "description": "Updated: Critical low balance",
  "condition": {
    "field": "balance",
    "operator": "<",
    "value": 50.00,
    "precision": 100
  }
}

Delete a monitor

DELETE /balance-monitors/mon_abc123

Webhook payload structure

When a monitor condition is met, Blnk sends a webhook:
{
  "event": "balance.monitor",
  "data": {
    "monitor_id": "mon_abc123xyz",
    "balance_id": "bln_customer_wallet",
    "description": "Alert when balance drops below $100",
    "condition": {
      "field": "balance",
      "operator": "<",
      "value": 100.00,
      "precision": 100,
      "precise_value": "10000"
    },
    "current_balance": {
      "balance": "9500",
      "credit_balance": "50000",
      "debit_balance": "40500",
      "inflight_balance": "0"
    },
    "triggered_at": "2024-01-15T14:30:00Z"
  }
}

Implementation details (from balance.go:59-86)

How Blnk checks balance monitors:
func (l *Blnk) checkBalanceMonitors(ctx context.Context, updatedBalance *model.Balance) {
    _, span := balanceTracer.Start(ctx, "CheckBalanceMonitors")
    defer span.End()

    // Fetch monitors using cache (avoids DB query on every transaction)
    monitors, err := l.getBalanceMonitorsCached(ctx, updatedBalance.BalanceID)
    if err != nil {
        span.RecordError(err)
        notification.NotifyError(err)
        return
    }

    // Check each monitor's condition
    for _, monitor := range monitors {
        if monitor.CheckCondition(updatedBalance) {
            span.AddEvent(fmt.Sprintf("Condition met for balance: %s", monitor.MonitorID))
            go func(monitor model.BalanceMonitor) {
                err := l.SendWebhook(NewWebhook{
                    Event:   "balance.monitor",
                    Payload: monitor,
                })
                if err != nil {
                    notification.NotifyError(err)
                }
            }(monitor)
        }
    }
}
Monitors are checked automatically after every transaction that affects the balance.

Performance optimization

Blnk caches balance monitors with a 5-minute TTL:
func (l *Blnk) getBalanceMonitorsCached(ctx context.Context, balanceID string) ([]model.BalanceMonitor, error) {
    cacheKey := "monitors:" + balanceID

    var monitors []model.BalanceMonitor
    err := l.cache.Get(ctx, cacheKey, &monitors)
    if err == nil && monitors != nil {
        return monitors, nil
    }

    monitors, err = l.datasource.GetBalanceMonitors(balanceID)
    if err != nil {
        return nil, err
    }

    _ = l.cache.Set(ctx, cacheKey, monitors, 5*time.Minute)
    return monitors, nil
}
From balance.go:99-119

Best practices

Use appropriate operators

Choose operators that match your use case (e.g., < for low balance, > for high activity)

Set realistic thresholds

Base threshold values on actual usage patterns

Avoid monitor spam

Don’t set thresholds that trigger too frequently

Include descriptions

Write clear descriptions for easier debugging

Monitor critical balances

Set up monitors for high-value or sensitive accounts

Handle webhooks reliably

Implement idempotent webhook handlers

Multi-threshold monitoring

Set up multiple monitors for different alert levels:
// Warning level
POST /balance-monitors
{
  "balance_id": "bln_wallet_001",
  "description": "Low balance warning",
  "condition": {
    "field": "balance",
    "operator": "<",
    "value": 100.00,
    "precision": 100
  }
}

// Critical level
POST /balance-monitors
{
  "balance_id": "bln_wallet_001",
  "description": "Critical low balance",
  "condition": {
    "field": "balance",
    "operator": "<",
    "value": 25.00,
    "precision": 100
  }
}

// Emergency level
POST /balance-monitors
{
  "balance_id": "bln_wallet_001",
  "description": "Emergency - near zero balance",
  "condition": {
    "field": "balance",
    "operator": "<",
    "value": 5.00,
    "precision": 100
  }
}

Troubleshooting

Monitor not triggering

Problem: Created a monitor but not receiving webhooks Checklist:
  1. Verify webhook URL is configured in blnk.json
  2. Check condition is actually being met
  3. Verify webhook endpoint is accessible
  4. Check webhook logs for errors

Duplicate notifications

Problem: Receiving multiple webhook notifications for same event Solution: Monitors trigger on every transaction that meets the condition. Implement webhook deduplication:
const processedMonitors = new Set();

function handleWebhook(payload) {
  const key = `${payload.data.monitor_id}_${payload.data.triggered_at}`;
  
  if (processedMonitors.has(key)) {
    return; // Skip duplicate
  }
  
  processedMonitors.add(key);
  // Process webhook
}

Next steps

Webhooks

Learn how to handle balance monitor webhooks

Wallet Management

Implement automated top-ups based on monitors

Build docs developers (and LLMs) love