# Create Monitoring Schedule Source: https://docs.rightfoot.com/api-reference/monitoring/create-scheduled-balance-request POST /v1/monitoring/scheduled_balance_request Create a recurring scheduled balance request for authorizers # Create Monitoring Schedule Create a recurring scheduled balance request for a set of authorizers. The schedule will automatically run balance checks at the specified interval and send results to your webhook. ## Scheduling Options Use `interval_days` together with `time_of_day` to run balance checks every N days at a specific time. | Field | Description | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `interval_days` | Run every N days (1-30) | | `time_of_day` | Time to run in HH:mm format (24-hour, UTC). Example: `"14:30"` for 2:30 PM UTC. If not provided, defaults to a random time in the 8:00 AM - 11:00 AM UTC window. | ## Key Information
**Schedule Name (Required):** Provide a `name` to identify your schedule. Names must be unique (max 255 characters).
**Start:** The first job runs at the `time_of_day` on the `starts_at` date, after which jobs will run on the provided interval (format: `YYYY-MM-DD`). If none is provided, the first job will run at the next `time_of_day`.
**Expiration:** A final job runs at the scheduled `time_of_day` on the `expires_at` date, after which no more jobs will run (format: `YYYY-MM-DD`, default 30 days from creation, max 365 days)
**Webhook:** Results are delivered to your specified webhook URL after each run
## Threshold Monitoring Set balance thresholds to receive webhook notifications when an account's balance meets or exceeds a specified amount. | Field | Level | Description | | ------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `threshold_amount` | Request | Applies to all authorizers in the batch. When any authorizer's balance meets or exceeds this amount, a webhook notification is sent. | | `threshold_amount` | Authorizer | Set within each authorizer object to define individual thresholds. Overrides the request-level threshold if both are provided. | When any threshold is configured (request-level or authorizer-level), the `webhook` field is required. ## Payment Information Types You must provide either **bank account information** or **debit card information** for each authorizer: ### Bank Account Information * Required: `account_number`, `routing_number`, and either `ssn` or `ein` ### Debit Card Information * Required: `debit_card_number`, `cvv`, `expiration_year`, `expiration_month`, and either `ssn` or `ein` ### Optional Fields * `threshold_amount`: Balance threshold in dollars for this authorizer (see [Threshold Monitoring](#threshold-monitoring)) # Retrieve Authorizer Balance History Source: https://docs.rightfoot.com/api-reference/monitoring/get-authorizer-history GET /v1/monitoring/authorizers/{id}/history Retrieve balance check history for a specific authorizer # Retrieve Authorizer Balance History Retrieve the balance check history for a specific authorizer. Returns a list of recent balance results in reverse chronological order (most recent first). ## Use Cases * Review historical balance data for an authorizer * Debug failed balance checks * Analyze balance trends over time ## Response Fields | Field | Description | | ---------------------- | ---------------------------------------------------------- | | `authorizer_unique_id` | The authorizer identifier | | `total_count` | Total number of history records for this authorizer | | `history` | Array of history entries (see below) | | `next_page_token` | Token for fetching the next page (null if no more results) | | `has_more` | Whether more results are available | Each history entry contains: | Field | Description | | ---------------- | -------------------------------------- | | `run_at` | When the balance check was performed | | `batch_id` | The associated batch request ID | | `balance_amount` | The balance retrieved (null if failed) | | `status` | `success` or `failed` | | `error_message` | Error details if the check failed | ## Pagination Use the `limit` query parameter to control how many results are returned: * Default: 10 entries * Maximum: 100 entries To fetch additional pages, pass the `next_page_token` from a previous response as the `next_page_token` query parameter. Continue fetching until `has_more` is `false` or `next_page_token` is null. # Retrieve Schedule Details Source: https://docs.rightfoot.com/api-reference/monitoring/get-schedule GET /v1/monitoring/schedules/{id} Retrieve details about a scheduled balance request # Retrieve Schedule Details Retrieve details about a scheduled balance request, including when it last ran, when it will run next, and how many authorizers are included in the schedule. ## Use Cases * Monitor the status of your scheduled balance requests * Verify when the next balance check will occur * Check how many authorizers are being tracked ## Response Fields | Field | Description | | ------------------- | ---------------------------------------------------------- | | `schedule_id` | The unique identifier for the schedule | | `name` | The name of the schedule, if one was provided | | `last_run_at` | When the schedule last completed a run (null if never run) | | `next_run_at` | When the next scheduled run will occur | | `total_authorizers` | Number of authorizers in this schedule | # Remove Schedule Authorizers Source: https://docs.rightfoot.com/api-reference/monitoring/remove-schedule-authorizers PATCH /v1/monitoring/schedules/{id}/authorizers Remove authorizers from a scheduled balance request # Remove Schedule Authorizers Remove specific authorizers from an existing scheduled balance request. The schedule will continue running for any remaining authorizers. ## Use Cases * Stop monitoring specific accounts while keeping the schedule active for others * Clean up authorizers that are no longer needed * Reduce the scope of an existing schedule without recreating it ## Response Fields | Field | Description | | ----------------- | ------------------------------------------------------------ | | `removed_count` | Number of authorizers successfully removed from the schedule | | `remaining_count` | Number of authorizers still in the schedule after removal | # Update Schedule Source: https://docs.rightfoot.com/api-reference/monitoring/update-schedule PATCH /v1/monitoring/schedules/{id} Update an existing scheduled balance request # Update Schedule Modify the scheduling parameters of an existing scheduled balance request. You can update the interval, time of day, or expiration date. ## Use Cases * Change how frequently balance checks run (e.g., from weekly to daily) * Adjust the time of day when scheduled checks execute * Extend or shorten the schedule's expiration date ## Request Fields All fields are optional, but at least one must be provided. | Field | Description | | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | Update the schedule name. Must be unique. Max 255 characters | | `interval_days` | Update how often the schedule runs (1-30 days) | | `time_of_day` | Update the time to run in HH:mm format (24-hour, UTC). Example: "14:30" for 2:30 PM UTC | | `expires_at` | Update the schedule expiration date. A final job runs at this time, after which no more jobs will run. Must be in the future. ISO 8601 format | ## Response Fields | Field | Description | | ------------- | --------------------------------------------------------------------------------- | | `schedule_id` | The unique identifier for the schedule | | `name` | The name of the schedule, if one was provided | | `message` | Confirmation message indicating the update was successful | | `expires_at` | The updated expiration date for the schedule | | `next_run_at` | The next scheduled run time. May be null if the schedule is disabled or completed | ## Important Notes * The schedule must be in **ACTIVE** status to be updated * At least one update field must be provided in the request * The `expires_at` value must be a future timestamp * If you provide a `name` that already exists for another schedule, a **409 Conflict** error is returned # Retrieve Balance Results Source: https://docs.rightfoot.com/api-reference/stable/get-balances GET /v1/balances Retrieve processed balances for a batch # Retrieve Balance Results Retrieve processed balance results for a previously submitted batch. This endpoint supports pagination to handle large result sets efficiently. ## Balance Status Codes The `balance_status_code` field indicates the outcome of the balance check: | Code | Description | | ------ | --------------------------------------------- | | `0` | Success - Balance retrieved successfully | | `3000` | No tax ID match | | `3001` | No SSN match | | `3100` | No date of birth match | | `3200` | Account closed | | `3300` | Institution not supported | | `3310` | Institution currently in testing | | `3400` | No match with provided authorizer information | | `9000` | Other error | A `null` balance with a non-zero status code indicates that the balance check failed for the specified reason. ## Pagination For batches with more results than the specified limit, use pagination: ```python Python theme={null} import requests def get_all_balances(batch_id, api_key): balances = [] next_page_token = None while True: url = f"https://api.rightfoot.com/v1/balances?batchId={batch_id}&limit=100" if next_page_token: url += f"&next_page_token={next_page_token}" headers = {"Authorization": f"Bearer {api_key}"} response = requests.get(url, headers=headers) data = response.json() balances.extend(data['balances']) if not data['has_more']: break next_page_token = data['next_page_token'] return balances ``` ```javascript JavaScript theme={null} async function getAllBalances(batchId, apiKey) { let balances = []; let nextPageToken = null; while (true) { let url = `https://api.rightfoot.com/v1/balances?batchId=${batchId}&limit=100`; if (nextPageToken) { url += `&nextPageToken=${nextPageToken}`; } const response = await fetch(url, { headers: { 'Authorization': `Bearer ${apiKey}` } }); const data = await response.json(); balances = balances.concat(data.balances); if (!data.has_more) { break; } nextPageToken = data.next_page_token; } return balances; } ``` ## Best Practices 1. **Poll periodically** - If processing is not complete, poll every 30-60 seconds 2. **Handle pagination** - Always check `has_more` and use `next_page_token` for complete results 3. **Process status codes** - Check `balance_status_code` to understand failed balance checks 4. **Implement retries** - Add exponential backoff for transient errors (5xx status codes) # Retrieve Balance Results (CSV) Source: https://docs.rightfoot.com/api-reference/stable/get-balances-csv GET /v1/balances/csv Download balance results for one or more batches as a CSV file # Retrieve Balance Results (CSV) Download balance results for one or more batches as a CSV file. The CSV includes the same columns produced by the Bigfoot dashboard download (`Institution`, `Phone`, `Zip`, `Creator`) in addition to the core fields returned by [Retrieve Balance Results](/api-reference/stable/get-balances). This endpoint returns a richer field set than `GET /v1/balances`. Use it when you need the full dashboard-style export. For paginated JSON per row, use `GET /v1/balances`. ## Multiple batches in a single export Repeat the `batch_ids` query parameter to fetch multiple batches in one CSV. All batches must belong to the authenticated requester — if any do not, the request is rejected with `403` and no file is returned. ```bash cURL theme={null} curl -X GET 'https://api.rightfoot.com/v1/balances/csv?batch_ids=8ac1f4a6-1234-4abc-9d3e-001122334455&batch_ids=12c4d6ee-aaaa-4abc-9d3e-bbbbccccdddd' \ -H 'Authorization: Bearer rf_live_...' \ -o balances.csv ``` ```python Python theme={null} import requests def download_balances_csv(batch_ids, api_key, output_path="balances.csv"): url = "https://api.rightfoot.com/v1/balances/csv" params = [("batch_ids", bid) for bid in batch_ids] headers = {"Authorization": f"Bearer {api_key}"} with requests.get(url, params=params, headers=headers, stream=True) as response: response.raise_for_status() with open(output_path, "wb") as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) return output_path ``` ```javascript JavaScript theme={null} import fs from "node:fs"; async function downloadBalancesCsv(batchIds, apiKey, outputPath = "balances.csv") { const params = new URLSearchParams(); for (const id of batchIds) { params.append("batch_ids", id); } const response = await fetch( `https://api.rightfoot.com/v1/balances/csv?${params.toString()}`, { headers: { Authorization: `Bearer ${apiKey}` } } ); if (!response.ok) { throw new Error(`Request failed: ${response.status} ${response.statusText}`); } const csv = await response.text(); fs.writeFileSync(outputPath, csv); return outputPath; } ``` ## Response Returns `text/csv; charset=utf-8` with `Content-Disposition: attachment; filename="batch-export-.csv"`. The CSV contains a header row followed by one row per balance result. The first nine columns are fixed; additional `meta_` columns are appended alphabetically when any authorizer in the result set has metadata. | Column | Description | | --------------- | -------------------------------------------------------------------------------------------------------------------------- | | `Authorizer ID` | Your authorizer's unique identifier. | | `Batch ID` | The batch this result belongs to. | | `Institution` | Full name of the financial institution. | | `Phone` | Authorizer phone number, if known. | | `Zip` | Authorizer zip code, if known. | | `Balance` | Account balance in USD (decimal). Empty when unavailable. | | `Status` | One of `SUCCESS`, `NO_MATCH`, `ACCOUNT_CLOSED`, `BANK_UNSUPPORTED`, `RETRIEVAL_FAILED`, `RETRY_LIMIT`, `PROCESSING_ERROR`. | | `Creator` | Display name of the dashboard user who submitted the request. Empty for API submissions. | | `Timestamp` | ISO-8601 timestamp (UTC) when the result was recorded. | | `meta_` | One column per metadata key set on any authorizer in the result. | ### Example response ```csv theme={null} Authorizer ID,Batch ID,Institution,Phone,Zip,Balance,Status,Creator,Timestamp,meta_customer_ref authz-001,8ac1f4a6-1234-4abc-9d3e-001122334455,Bank of America,+15551234567,10001,2843.51,SUCCESS,jane.doe@acme.com,2026-05-17T19:24:12+00:00,CR-9981 authz-002,8ac1f4a6-1234-4abc-9d3e-001122334455,Wells Fargo,,90210,,NO_MATCH,,2026-05-17T19:25:03+00:00, ``` ## Errors | Status | Cause | | ------ | -------------------------------------------------------------------------- | | `400` | Missing `batch_ids` or one of the values is not a valid UUID. | | `401` | Missing or invalid API key. | | `403` | One or more requested batches don't belong to the authenticated requester. | | `500` | Internal server error. | ## Best Practices 1. **Stream to disk** - CSV exports can be large; use `-o` (cURL) or stream the response body rather than buffering it in memory 2. **Match by `Authorizer ID`** - Pair rows back to your records using the `Authorizer ID` column, which mirrors the `authorizer_unique_id` you submitted 3. **Check `Status` per row** - An empty `Balance` paired with a non-`SUCCESS` `Status` indicates the balance check failed for that authorizer # Submit Balance Check Source: https://docs.rightfoot.com/api-reference/stable/submit-balance-request POST /v1/balance_requests Submit a batch of authorizers for balance checks # Submit Balance Check Submit a batch of authorizers for balance checks. This endpoint allows you to submit up to 1,000 authorizers in a single request to retrieve their current account balances. **Debit Card Information:** Balance requests using debit card information is currently restricted to approved customers only. If you would like to learn more, please reach out to us at [sales@rightfoot.com](mailto:sales@rightfoot.com). ## Key Information
**Batch Size Limit:** Maximum 1,000 authorizers per request
**Idempotency:** Requests are cached for 24 hours based on the request body hash. Submitting an identical batch within this period will return the same response without reprocessing
## Important Notes We ask that you schedule the API call with sufficient time prior to ACH cut-offs to retrieve the balance results and omit authorizers with insufficient balances for the loan payment. ## Payment Information Types You must provide either **bank account information** or **debit card information** (approved customers only): ### Bank Account Information * Required: `account_number`, `routing_number`, and either `ssn` or `ein` ### Debit Card Information * Required: `debit_card_number`, `cvv`, `expiration_year`, `expiration_month`, and either `ssn` or `ein` * Optional: `routing_number` (if linked to a bank account) * **Note:** Debit card balance checks require prior approval # Submit Balance Check (CSV) Source: https://docs.rightfoot.com/api-reference/stable/submit-batch-csv POST /v1/balance_requests/csv Submit a batch of authorizers for balance checks via CSV upload # Submit Balance Check (CSV) Upload a CSV file of authorizers for balance checks. This endpoint is the file-upload counterpart to [Submit Balance Check](/api-reference/stable/submit-balance-request) — use it when your authorizer list is already exported as a spreadsheet. **Debit Card Information:** Balance requests using debit card information is currently restricted to approved customers only. If you would like to learn more, please reach out to us at [sales@rightfoot.com](mailto:sales@rightfoot.com). ## Key Information
**Batch Size Limit:** Maximum 5,000 rows per CSV
**Content-Type:** `multipart/form-data` with the file part as `text/csv`
**Encoding:** UTF-8 preferred. Files exported from Excel on Windows are decoded as Latin-1 with a `warnings` entry on the response.
**Validation:** Per-row validation runs synchronously at request time. Failures are returned inline in the response.
## CSV Format The header row must use the column names below. Required and optional columns differ slightly depending on whether you submit **bank-account** or **debit-card** info — provide exactly one per row, never both. ### Always Required (every row) | Column | Format | | ---------------------- | ----------------------------------------------------------------------------------- | | `authorizer_unique_id` | Alphanumeric, must be unique within the CSV | | `phone_number` | Exactly 10 digits, no separators (e.g. `5551234567`) | | `ssn` *or* `ein` | At least one. `ssn`: 9 digits or `XXX-XX-XXXX`. `ein`: 9–10 digits or `XX-XXXXXXX`. | ### Bank-Account Mode Provide `account_number`: | Column | Required? | Format | | ---------------- | --------- | ---------------- | | `account_number` | required | Free-form string | | `routing_number` | required | 9 digits | ### Debit-Card Mode Provide `debit_card_number` (approved customers only): | Column | Required? | Format | | ------------------- | --------- | ------------ | | `debit_card_number` | required | 16 digits | | `cvv` | optional | 3–4 digits | | `expiration_month` | optional | Integer 1–12 | | `expiration_year` | optional | 4 digits | | `routing_number` | optional | 9 digits | Submitting both `account_number` and `debit_card_number` on the same row is rejected. Pick one mode per row. ### Always Optional (any row) | Column | Format | | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | `first_name`, `last_name` | Free-form | | `street_address_line1`, `street_address_line2`, `city`, `state`, `country` | Free-form | | `zip_code` | 5 digits or `ZZZZZ-NNNN`. **Required for Discover** (routing number `031100649`). | | `date_of_birth` | `MM/DD/YYYY`. If provided, all three components must be present and year must be ≥ 1900. | | `meta_*` | Any column prefixed with `meta_` is collected into a per-row `metadata` object (the `meta_` prefix is stripped). | ## Validation Behavior Validation runs in-process at request time — no PII is staged on the batch row. * **Mixed result (some rows valid, some invalid):** the batch is created with the valid subset, and per-row failures are returned inline in `failed_authorizers`. * **All rows invalid:** the request returns `400 Bad Request` with the per-row failures in `error.details.failed_authorizers`. No batch is created. * **Empty file or wrong content type:** the request returns `400 Bad Request` before any rows are read. # Balance Check Run Completed Source: https://docs.rightfoot.com/api-reference/webhooks/balance-batch-completed WEBHOOK balance_check.run.completed Fires when a balance check run completes # Balance Check Run Completed Webhook Fires when a balance check run completes. A notification will be sent to the `webhook_url` specified in the `/v1/balance_requests` endpoint. **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 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). ## Webhook Response Your webhook endpoint should respond with an HTTP `2xx` status code to confirm receipt: ```json theme={null} { "status": "received" } ``` If a `2xx` response is not received, the webhook will be retried with exponential backoff for up to 30 minutes. ## Implementing a Webhook Endpoint Here are examples of implementing a webhook endpoint in various languages: ```python Python (Flask) theme={null} from flask import Flask, request, jsonify import logging app = Flask(__name__) @app.route('/webhook/balance-batch-complete', methods=['POST']) def handle_balance_webhook(): 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']): return jsonify({'error': 'Missing required fields'}), 400 # Verify the event type if data['type'] != 'BALANCE_BATCH_COMPLETED': return jsonify({'error': 'Unexpected event type'}), 400 # Log the event logging.info(f"Balance batch completed: {data['batch_id']}") # Process the completed batch (fetch results, update database, etc.) process_completed_batch(data['batch_id']) # 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_completed_batch(batch_id): # Fetch balance results using the batch_id # Update your database # Trigger any downstream processes pass ``` ```javascript Node.js (Express) theme={null} const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhook/balance-batch-complete', async (req, res) => { try { const { event_uuid, type, batch_id } = req.body; // Validate required fields if (!event_uuid || !type || !batch_id) { return res.status(400).json({ error: 'Missing required fields' }); } // Verify the event type if (type !== 'BALANCE_BATCH_COMPLETED') { return res.status(400).json({ error: 'Unexpected event type' }); } // Log the event console.log(`Balance batch completed: ${batch_id}`); // Process the completed batch await processCompletedBatch(batch_id); // 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 processCompletedBatch(batchId) { // Fetch balance results using the batchId // Update your database // Trigger any downstream processes } app.listen(3000, () => { console.log('Webhook server listening on port 3000'); }); ``` ```php PHP theme={null} 'Invalid JSON payload']); exit; } // Validate required fields if (!isset($data['event_uuid']) || !isset($data['type']) || !isset($data['batch_id'])) { http_response_code(400); echo json_encode(['error' => 'Missing required fields']); exit; } // Verify the event type if ($data['type'] !== 'BALANCE_BATCH_COMPLETED') { http_response_code(400); echo json_encode(['error' => 'Unexpected event type']); exit; } // Log the event error_log("Balance batch completed: " . $data['batch_id']); try { // Process the completed batch processCompletedBatch($data['batch_id']); // 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 processCompletedBatch($batchId) { // Fetch balance results using the batchId // Update your database // Trigger any downstream processes } ?> ``` ## Retry Mechanism The webhook delivery system implements exponential backoff: 1. **Initial attempt** - Immediate 2. **First retry** - After 1 minute 3. **Second retry** - After 2 minutes 4. **Third retry** - After 4 minutes 5. **Subsequent retries** - Doubling interval up to 30 minutes total After 30 minutes, no further retry attempts will be made. ## Security Considerations To secure your webhook endpoint: 1. **Use HTTPS** - Always use SSL/TLS encryption for your webhook endpoint 2. **Validate payloads** - Check that all required fields are present 3. **Implement idempotency** - Handle duplicate webhook deliveries gracefully 4. **Add authentication** - Consider implementing webhook signatures or API keys 5. **Rate limiting** - Protect against potential abuse ## Fallback Strategy Since webhook delivery is not guaranteed, implement a polling fallback: ```python Python theme={null} import time import requests def wait_for_batch_completion(batch_id, api_key, max_wait=3600): """ Poll for batch completion with exponential backoff """ start_time = time.time() poll_interval = 30 # Start with 30 seconds while time.time() - start_time < max_wait: # Check if batch is complete response = requests.get( f"https://api.rightfoot.com/v1/balances?batchId={batch_id}&limit=1", headers={"Authorization": f"Bearer {api_key}"} ) if response.status_code == 200: # Batch is complete return True elif response.status_code == 404: # Batch still processing time.sleep(poll_interval) poll_interval = min(poll_interval * 1.5, 300) # Cap at 5 minutes else: # Handle error raise Exception(f"Error checking batch status: {response.status_code}") return False # Timeout reached ``` ```javascript JavaScript theme={null} async function waitForBatchCompletion(batchId, apiKey, maxWait = 3600) { const startTime = Date.now(); let pollInterval = 30000; // Start with 30 seconds while ((Date.now() - startTime) / 1000 < maxWait) { try { const response = await fetch( `https://api.rightfoot.com/v1/balances?batchId=${batchId}&limit=1`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); if (response.status === 200) { // Batch is complete return true; } else if (response.status === 404) { // Batch still processing await new Promise(resolve => setTimeout(resolve, pollInterval)); pollInterval = Math.min(pollInterval * 1.5, 300000); // Cap at 5 minutes } else { // Handle error throw new Error(`Error checking batch status: ${response.status}`); } } catch (error) { throw error; } } return false; // Timeout reached } ``` ## Best Practices 1. **Always implement polling** - Don't rely solely on webhooks 2. **Handle duplicates** - Your system should be idempotent 3. **Log all events** - Keep an audit trail of webhook receipts 4. **Monitor failures** - Set up alerts for webhook processing errors 5. **Process asynchronously** - Return `200` quickly and process in the background # Balance Threshold Alert Source: https://docs.rightfoot.com/api-reference/webhooks/balance-threshold-alert WEBHOOK 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. **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 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). ## 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" } ``` If a `2xx` response is not received, the webhook will be retried with exponential backoff for up to 30 minutes. ## Implementing a Webhook Endpoint Here are examples of implementing a webhook endpoint to handle threshold alerts: ```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} '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 } ?> ``` ## 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 # IP Allowlisting Source: https://docs.rightfoot.com/api-reference/webhooks/ip-allowlist Static IP addresses Rightfoot uses to send webhooks If your webhook endpoint is behind a firewall or restricts inbound traffic by IP, you'll need to allowlist Rightfoot's outbound IP addresses. Otherwise, webhook deliveries will be blocked and your endpoint will not receive events. ## Webhook source IPs Rightfoot sends webhooks from the following static IP addresses. Add **all** of them to your allowlist, as we run multiple services that may originate webhook traffic: ```text theme={null} 35.254.143.17 35.223.223.146 ``` These IPs apply to all Rightfoot webhook events, including [Balance Check Run Completed](/api-reference/webhooks/balance-batch-completed) and [Balance Threshold Alert](/api-reference/webhooks/balance-threshold-alert). # Authentication Source: https://docs.rightfoot.com/authentication Learn how to authenticate with the Rightfoot API # Authentication ## BearerAuth **HTTP: BearerAuth** 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. **HTTP Authorization Scheme:** bearer ## Making Authenticated Requests Include your API key as a Bearer token in the Authorization header: ```bash cURL theme={null} curl -X POST https://api.rightfoot.com/v1/balance_requests \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "authorizers": [...] }' ``` ```python Python theme={null} import requests headers = { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" } response = requests.post( "https://api.rightfoot.com/v1/balance_requests", headers=headers, json={"authorizers": [...]} ) ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.rightfoot.com/v1/balance_requests', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ authorizers: [...] }) }); ``` ## Error Responses If authentication fails, you'll receive a 401 response: ```json theme={null} { "status": "error", "status_code": 401, "error": { "code": "UNAUTHORIZED", "message": "Invalid API key provided.", "timestamp": "2024-09-16T12:01:00Z", "suggestion": "Please check your API key." }, "batch_id": null, "documentation_url": "https://api.rightfoot.com/docs" } ``` # Introduction Source: https://docs.rightfoot.com/introduction Welcome to the Rightfoot API documentation # Rightfoot API The Rightfoot API provides balance intelligence for payment and collections workflows. It allows you to check account balances on demand, set up recurring monitoring, and receive event-driven signals when balance check runs complete. Rightfoot helps you determine when to attempt payments and/or how to take action, while integrating cleanly with your existing systems. ## API Overview The Rightfoot API enables you to: * **Run on-demand balance checks** for individual or batched authorizers * **Retrieve balance check results** * **Create recurring monitoring schedules** to systematically observe account balances over time * **Define balance thresholds** and receive alerts when accounts have sufficient funds * **Access historical balance requests** at the authorizer level to support monitoring and analysis * **Receive webhooks** when balance check runs complete or threshold conditions are met The API supports both **one-time execution** and **recurring monitoring**. ## Execution Models Rightfoot supports two primary execution models: ### On-Demand Balance Checks Use on-demand endpoints to submit balance checks immediately and retrieve results once processing completes. This is ideal for ad-hoc checks, batch uploads, or manual workflows. ### Monitoring & Threshold Alerts Use monitoring endpoints to create schedules that run balance checks automatically on a recurring cadence. Thresholds can be applied to monitoring so your systems are notified when an account's balance meets or exceeds a defined amount. This enables proactive, event-driven decisioning without requiring constant polling. ## Events & Webhooks Rightfoot emits webhooks when: * A **balance check run completes** * A **threshold condition is triggered** for a monitored account These events allow downstream systems to fetch results and take action immediately. If your endpoint sits behind a firewall, allowlist Rightfoot's webhook IPs so deliveries aren't blocked. See [IP Allowlisting](/api-reference/webhooks/ip-allowlist). ## Base URL All API requests should be made to: ```text theme={null} https://api.rightfoot.com ``` ## Authentication The Rightfoot API uses Bearer token authentication. Include your API key in the Authorization header: ```bash theme={null} Authorization: Bearer YOUR_API_KEY ``` ## Rate Limits To ensure fair usage and system stability: * Maximum batch size: **1,000 authorizers** per request * Requests are cached for **24 hours** based on request body hash ## Support For questions or assistance: * For debit card balance requests: Contact [sales@rightfoot.com](mailto:sales@rightfoot.com) * General support: [support@rightfoot.com](mailto:support@rightfoot.com) ## Getting Started Set up your API key and make your first request Submit your first balance check batch # Quickstart Source: https://docs.rightfoot.com/quickstart Get started with the Rightfoot API in minutes # Quickstart Guide This guide will walk you through submitting your first balance check request and retrieving the results. ## Prerequisites Before you begin, make sure you have: * A Rightfoot API key * A tool to make HTTP requests (cURL, Postman, or your preferred programming language) ## Step 1: Submit a Balance Check Request Submit a batch of authorizers for balance checking: ```bash cURL theme={null} curl -X POST https://api.rightfoot.com/v1/balance_requests \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "authorizers": [ { "authorizer_unique_id": "1a2b3c4d", "first_name": "John", "last_name": "Doe", "address": { "street_address_line1": "123 Main St", "city": "Anytown", "state": "CA", "zip_code": "12345" }, "phone_number": "5551234567", "date_of_birth": { "day": 1, "month": 1, "year": 1980 }, "ssn": "123456789", "account_number": "1234567890", "routing_number": "021000021" } ], "webhook_url": "https://your-api.com/webhook" }' ``` ```python Python theme={null} import requests import json url = "https://api.rightfoot.com/v1/balance_requests" headers = { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" } data = { "authorizers": [ { "authorizer_unique_id": "1a2b3c4d", "first_name": "John", "last_name": "Doe", "address": { "street_address_line1": "123 Main St", "city": "Anytown", "state": "CA", "zip_code": "12345" }, "phone_number": "5551234567", "date_of_birth": { "day": 1, "month": 1, "year": 1980 }, "ssn": "123456789", "account_number": "1234567890", "routing_number": "021000021" } ], "webhook_url": "https://your-api.com/webhook" } response = requests.post(url, headers=headers, json=data) print(response.json()) ``` ### Response You'll receive a response with the batch ID: ```json theme={null} { "batch_id": "5d3c6bbb-fc1a-46a4-93da-1ce4a54b0d83", "submitted_at": "2024-09-17T10:00:00Z", "message": "Batch has been successfully submitted and is pending processing." } ``` Save the `batch_id` - you'll need it to retrieve the balance results. ## Step 2: Retrieve Balance Results After processing (typically within 1 hour), retrieve the balance results using the batch ID: ```bash cURL theme={null} curl -X GET "https://api.rightfoot.com/v1/balances?batchId=5d3c6bbb-fc1a-46a4-93da-1ce4a54b0d83" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests batch_id = "5d3c6bbb-fc1a-46a4-93da-1ce4a54b0d83" url = f"https://api.rightfoot.com/v1/balances?batchId={batch_id}" headers = { "Authorization": "Bearer YOUR_API_KEY" } response = requests.get(url, headers=headers) print(response.json()) ``` ### Response ```json theme={null} { "batch_id": "5d3c6bbb-fc1a-46a4-93da-1ce4a54b0d83", "total_processed": 1, "balances": [ { "authorizer_unique_id": "1a2b3c4d", "balance": 3851.09, "timestamp": "2024-08-09T17:51:34Z", "balance_status_code": 0 } ], "has_more": false, "next_page_token": null } ``` ## Understanding Status Codes The `balance_status_code` indicates the outcome: | Code | Description | | ---- | ---------------------------------- | | 0 | Success - Balance retrieved | | 2560 | Invalid phone number | | 3000 | No tax ID match | | 3100 | No date of birth match | | 3200 | Account closed | | 3300 | Institution not supported | | 3400 | No match with provided information | | 9000 | Other error | ## Important Notes * **Batch Size Limit**: Maximum 1,000 authorizers per request * **Idempotency**: Identical requests within 24 hours return cached results ## Using Webhooks (Optional) If you provided a `webhook_url`, you'll receive a notification when processing completes: ```json theme={null} { "event_uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "type": "BALANCE_BATCH_COMPLETED", "batch_id": "5d3c6bbb-fc1a-46a4-93da-1ce4a54b0d83" } ``` Webhook delivery is attempted for up to 30 minutes with exponential backoff. Always implement polling as a fallback. ## Next Steps Now that you've successfully submitted your first balance check, explore our API endpoints: Learn about all parameters and options for balance checks Retrieve and paginate through balance results