> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rightfoot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Balance Threshold Alert

> Webhook notification when authorizers meet or exceed configured balance thresholds

# Balance Threshold Alert Webhook

When a scheduled balance check finds authorizers whose balance meets or exceeds their configured threshold, a notification is sent to the `webhook` URL specified when creating the monitoring schedule.

<Warning>
  **Important Considerations:**

  * Webhook delivery is not guaranteed
  * Webhooks will be retried for up to 30 minutes with exponential backoff
  * If delivery fails after 30 minutes, no further retries will occur
  * Always implement polling as a fallback mechanism
</Warning>

<Note>
  If your webhook endpoint restricts inbound traffic by IP, make sure to allowlist Rightfoot's outbound IPs. See [IP Allowlisting](/api-reference/webhooks/ip-allowlist).
</Note>

## Payload Structure

```json theme={null}
{
  "event_uuid": "7b6ff2ac-4779-4893-b5a8-d2c726d366bd",
  "type": "BALANCE_THRESHOLD_ALERT",
  "schedule_id": "6ddd3761-bb1c-4538-b278-09717a2e8bba",
  "batch_id": "d0b12bb3-ea9e-49fb-bd13-21be687cfc04",
  "alerts": [
    {
      "authorizer_unique_id": "user-123",
      "institution_name": "First National Bank",
      "balance": 150.00,
      "threshold_amount": 100.00
    },
    {
      "authorizer_unique_id": "user-456",
      "institution_name": "Community Credit Union",
      "balance": 250.50,
      "threshold_amount": 200.00
    }
  ],
  "timestamp": "2024-01-15T10:30:00Z"
}
```

## Payload Fields

| Field                           | Type   | Description                                                      |
| ------------------------------- | ------ | ---------------------------------------------------------------- |
| `event_uuid`                    | string | Unique identifier for this webhook event                         |
| `type`                          | string | Always `BALANCE_THRESHOLD_ALERT` for this event type             |
| `schedule_id`                   | string | The monitoring schedule that triggered the alert                 |
| `batch_id`                      | string | The batch ID for this balance check run                          |
| `alerts`                        | array  | List of authorizers that met or exceeded their threshold         |
| `alerts[].authorizer_unique_id` | string | Your unique identifier for the authorizer                        |
| `alerts[].institution_name`     | string | The name of the financial institution this authorizer is tied to |
| `alerts[].balance`              | number | The authorizer's current balance in dollars                      |
| `alerts[].threshold_amount`     | number | The configured threshold in dollars                              |
| `timestamp`                     | string | ISO 8601 timestamp when the event was created                    |

## Webhook Response

Your webhook endpoint should respond with an HTTP `2xx` status code to confirm receipt:

```json theme={null}
{
  "status": "received"
}
```

<Note>
  If a `2xx` response is not received, the webhook will be retried with exponential backoff for up to 30 minutes.
</Note>

## Implementing a Webhook Endpoint

Here are examples of implementing a webhook endpoint to handle threshold alerts:

<CodeGroup>
  ```python Python (Flask) theme={null}
  from flask import Flask, request, jsonify
  import logging

  app = Flask(__name__)

  @app.route('/webhook/threshold-alert', methods=['POST'])
  def handle_threshold_alert():
      try:
          # Parse the webhook payload
          data = request.get_json()

          # Validate required fields
          if not all(k in data for k in ['event_uuid', 'type', 'batch_id', 'alerts']):
              return jsonify({'error': 'Missing required fields'}), 400

          # Verify the event type
          if data['type'] != 'BALANCE_THRESHOLD_ALERT':
              return jsonify({'error': 'Unexpected event type'}), 400

          # Process each alert
          for alert in data['alerts']:
              logging.info(
                  f"Threshold met: {alert['authorizer_unique_id']} "
                  f"has ${alert['balance']} (threshold: ${alert['threshold_amount']})"
              )
              # Take action - initiate payment, send notification, etc.
              process_threshold_alert(alert)

          # Return success response
          return jsonify({'status': 'received'}), 200

      except Exception as e:
          logging.error(f"Webhook processing error: {str(e)}")
          return jsonify({'error': 'Internal server error'}), 500

  def process_threshold_alert(alert):
      # Initiate payment collection
      # Update your database
      # Send notifications
      pass
  ```

  ```javascript Node.js (Express) theme={null}
  const express = require('express');
  const app = express();

  app.use(express.json());

  app.post('/webhook/threshold-alert', async (req, res) => {
    try {
      const { event_uuid, type, batch_id, alerts } = req.body;

      // Validate required fields
      if (!event_uuid || !type || !batch_id || !alerts) {
        return res.status(400).json({ error: 'Missing required fields' });
      }

      // Verify the event type
      if (type !== 'BALANCE_THRESHOLD_ALERT') {
        return res.status(400).json({ error: 'Unexpected event type' });
      }

      // Process each alert
      for (const alert of alerts) {
        console.log(
          `Threshold met: ${alert.authorizer_unique_id} ` +
          `has $${alert.balance} (threshold: $${alert.threshold_amount})`
        );
        // Take action - initiate payment, send notification, etc.
        await processThresholdAlert(alert);
      }

      // Return success response
      res.status(200).json({ status: 'received' });

    } catch (error) {
      console.error('Webhook processing error:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  });

  async function processThresholdAlert(alert) {
    // Initiate payment collection
    // Update your database
    // Send notifications
  }

  app.listen(3000, () => {
    console.log('Webhook server listening on port 3000');
  });
  ```

  ```php PHP theme={null}
  <?php
  header('Content-Type: application/json');

  // Get the webhook payload
  $payload = file_get_contents('php://input');
  $data = json_decode($payload, true);

  // Validate JSON parsing
  if (json_last_error() !== JSON_ERROR_NONE) {
      http_response_code(400);
      echo json_encode(['error' => 'Invalid JSON payload']);
      exit;
  }

  // Validate required fields
  if (!isset($data['event_uuid']) || !isset($data['type']) ||
      !isset($data['batch_id']) || !isset($data['alerts'])) {
      http_response_code(400);
      echo json_encode(['error' => 'Missing required fields']);
      exit;
  }

  // Verify the event type
  if ($data['type'] !== 'BALANCE_THRESHOLD_ALERT') {
      http_response_code(400);
      echo json_encode(['error' => 'Unexpected event type']);
      exit;
  }

  try {
      // Process each alert
      foreach ($data['alerts'] as $alert) {
          error_log(sprintf(
              "Threshold met: %s has $%s (threshold: $%s)",
              $alert['authorizer_unique_id'],
              $alert['balance'],
              $alert['threshold_amount']
          ));
          // Take action - initiate payment, send notification, etc.
          processThresholdAlert($alert);
      }

      // Return success response
      http_response_code(200);
      echo json_encode(['status' => 'received']);

  } catch (Exception $e) {
      error_log("Webhook processing error: " . $e->getMessage());
      http_response_code(500);
      echo json_encode(['error' => 'Internal server error']);
  }

  function processThresholdAlert($alert) {
      // Initiate payment collection
      // Update your database
      // Send notifications
  }
  ?>
  ```
</CodeGroup>

## Best Practices

1. **Act on alerts promptly** - Threshold alerts indicate optimal payment timing
2. **Handle duplicates** - Your system should be idempotent
3. **Log all events** - Keep an audit trail of threshold alerts
4. **Monitor failures** - Set up alerts for webhook processing errors
5. **Process asynchronously** - Return `200` quickly and process in the background


## OpenAPI

````yaml WEBHOOK balance_threshold_alert
openapi: 3.1.0
info:
  title: Rightfoot API
  description: >-
    Submit a batch of authorizers for balance checks, retrieve processed
    balances, and check the status of a batch.
  version: 0.3.0-alpha
servers:
  - url: https://api.rightfoot.com
security:
  - BearerAuth: []
tags:
  - name: General Availability
    description: |
      **General Availability** indicates that the endpoint is production-ready.
  - name: Early Access
    description: >
      **Early Access** indicates that the endpoint is available for early access
      customers and is being actively refined.
  - name: In Development
    description: >
      **In Development** indicates that the endpoint is currently being built
      and will be available in an upcoming release.
paths: {}
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >
        Authentication to the API is performed via Bearer Token Authentication.
        Provide your API key as the bearer token in the Authorization header.


        All API requests must be made over HTTPS. Calls made over plain HTTP
        will fail. API requests without authentication will also fail.

````