Webhooks Documentation

Real-time rate alerts delivered to your endpoint. Set conditions, get notified when rates hit your thresholds - with full decision context.

Why Use Webhooks

Stop polling. Start reacting instantly when rates change.

Instant Notifications

Get alerts within seconds of a rate change, not hours later when your cron job runs. Sub-second delivery to your endpoint.

No Polling Required

Eliminate the infrastructure cost and complexity of constantly checking for rate changes. We monitor, you react.

Full Context Included

Every webhook includes the complete decision snapshot. Act immediately without making follow-up API calls.

Precise Conditions

Trigger only when it matters: APR below a threshold, rate drops by a percentage, or payment under a target amount.

Cryptographically Signed

Verify every webhook is authentic with HMAC-SHA256 signatures. Event IDs prevent replay attacks.

Test Before Production

Simulate webhook deliveries to verify your handler works correctly. Ship with confidence.

How It Works

From monitor creation to webhook delivery in four steps

1

Create Monitor

Set your conditions: state, product type, thresholds

->
2

We Watch Rates

RateAPI continuously monitors rate changes

->
3

Condition Met

Your threshold is triggered by a rate change

->
4

Webhook Fires

Full decision context delivered to your endpoint

webhook delivery
// Your endpoint receives a POST request
// Headers include signature for verification

POST https://your-app.com/webhooks/rateapi

X-RateAPI-Signature: sha256=abc123...
X-RateAPI-Timestamp: 1706234567
X-RateAPI-Event-Id: evt_xyz789

{
  "event": "decision.monitor.triggered",
  "trigger_type": "condition_met",
  "why_now": ["APR dropped below 6.25% threshold"],
  "decision_snapshot": { /* full /v1/decisions response */ }
}

Getting Started

Set up your first webhook monitor in minutes

1

Create an API Key

Generate an API key to authenticate your requests. Store it securely - you'll need it for all API calls and it can't be retrieved again.

terminal
curl -X POST https://api.rateapi.dev/keys

# Response
{
  "key": "rk_live_abc123...",
  "id": "key_xyz789"
}
2

Set Up a Webhook Endpoint

Create an HTTPS endpoint on your server to receive webhook payloads. The endpoint must respond with a 2xx status code within 5 seconds.

server.js
const express = require('express');
const app = express();

app.post('/webhooks/rateapi', express.json(), (req, res) => {
  const { event, trigger_type, why_now, decision_snapshot } = req.body;

  // Verify signature (see Security section)
  // Process the rate alert
  console.log(`Rate alert: ${why_now[0]}`);

  // Respond quickly to acknowledge receipt
  res.status(200).json({ received: true });
});
3

Create Your First Monitor

Define the conditions that should trigger a webhook. You can monitor specific states, product types, and set threshold conditions.

create monitor
curl -X POST https://api.rateapi.dev/v1/monitors \
  -H "Authorization: Bearer rk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CA Low APR Alert",
    "webhook_url": "https://your-app.com/webhooks/rateapi",
    "state": "CA",
    "product_type": "30yr_fixed",
    "loan_amount": 500000,
    "conditions": [
      {
        "field": "apr",
        "operator": "lt",
        "value": 6.25
      }
    ],
    "cooldown_minutes": 60
  }'

# Response
{
  "id": "mon_abc123",
  "status": "active",
  "webhook_secret": "whsec_xyz789..."
}
Save Your Webhook Secret

The webhook_secret is returned only once when you create the monitor. Store it securely - you'll need it to verify webhook signatures.

4

Test with Simulation

Before waiting for real rate changes, test your webhook handler using the simulate endpoint. This sends a test payload to your webhook URL.

simulate webhook
curl -X POST https://api.rateapi.dev/v1/monitors/mon_abc123/simulate \
  -H "Authorization: Bearer rk_live_abc123..."

# Response
{
  "success": true,
  "delivered": true,
  "response_code": 200,
  "response_time_ms": 124
}

The simulated webhook will include "test": true in the payload so your handler can distinguish test events from real alerts.

Webhook Payload Reference

Complete documentation of the webhook payload structure

webhook payload example
{
  "event": "decision.monitor.triggered",
  "event_id": "evt_abc123xyz789",
  "monitor_id": "mon_abc123",
  "customer_id": "key_xyz789",
  "evaluated_at": "2024-01-25T14:30:00Z",
  "trigger_type": "condition_met",
  "why_now": [
    "APR dropped below 6.25% threshold (now 6.125%)",
    "Navy Federal offering $127/mo savings vs current rate"
  ],
  "rate_change": {
    "state": "CA",
    "product_type": "30yr_fixed",
    "previous_rate": 6.375,
    "new_rate": 6.125,
    "rate_delta": -0.25
  },
  "condition_results": [
    {
      "field": "apr",
      "operator": "lt",
      "threshold": 6.25,
      "actual_value": 6.125,
      "passed": true
    }
  ],
  "decision_snapshot": {
    "summary": {
      "recommended_action": "shop_providers",
      "confidence": "high"
    },
    "actions": [
      {
        "type": "compare_offers",
        "offers": [...]
      }
    ]
  },
  "test": false
}

Top-Level Fields

Field Type Description
event string decision.monitor.triggered for real alerts, decision.monitor.test for simulations
event_id string Unique identifier for this event. Use for deduplication and idempotency.
monitor_id string The ID of the monitor that triggered this webhook
customer_id string Your API key ID (not the key itself)
evaluated_at ISO 8601 Timestamp when the conditions were evaluated
trigger_type string One of: condition_met, recommendation_changed, threshold_crossed, new_offer_detected
why_now string[] Human-readable explanations of why the webhook was triggered
test boolean true if this is a simulated webhook, false for real alerts

rate_change Object

Field Type Description
state string Two-letter state code (e.g., "CA", "TX")
product_type string Mortgage product type (e.g., "30yr_fixed", "15yr_fixed")
previous_rate number The rate before the change (percentage)
new_rate number The current rate after the change (percentage)
rate_delta number The change in rate (negative = decrease)

condition_results Array

Field Type Description
field string The field that was evaluated (rate, apr, monthly_payment)
operator string The comparison operator used
threshold number The threshold value from your condition
actual_value number The actual value at evaluation time
passed boolean Whether this condition passed

decision_snapshot Object

Contains the full response from the /v1/decisions endpoint at the time of evaluation. This includes recommended actions, top offers, savings calculations, and provider details. See the Decisions API documentation for the complete schema.

Webhook Signature Verification

Verify webhook authenticity using HMAC-SHA256 signatures

Security Headers

X-RateAPI-Signature HMAC-SHA256 signature of the request body using your webhook secret
X-RateAPI-Timestamp Unix timestamp when the webhook was sent. Use to prevent replay attacks.
X-RateAPI-Event-Id Unique event identifier. Store processed IDs for idempotency.

Node.js Verification

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  const actual = signature.replace('sha256=', '');

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(actual)
  );
}

// In your webhook handler:
app.post('/webhooks/rateapi', (req, res) => {
  const signature = req.headers['x-rateapi-signature'];
  const timestamp = req.headers['x-rateapi-timestamp'];

  // Reject old webhooks (> 5 minutes)
  if (Date.now() / 1000 - timestamp > 300) {
    return res.status(401).send('Expired');
  }

  if (!verifySignature(req.rawBody, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // Process webhook...
});

Python Verification

import hmac
import hashlib
import time

def verify_signature(payload, signature, secret):
    expected = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    actual = signature.replace('sha256=', '')

    return hmac.compare_digest(expected, actual)

# In your webhook handler:
from flask import Flask, request

@app.route('/webhooks/rateapi', methods=['POST'])
def webhook_handler():
    signature = request.headers.get('X-RateAPI-Signature')
    timestamp = request.headers.get('X-RateAPI-Timestamp')

    # Reject old webhooks (> 5 minutes)
    if time.time() - int(timestamp) > 300:
        return 'Expired', 401

    if not verify_signature(
        request.data.decode(),
        signature,
        WEBHOOK_SECRET
    ):
        return 'Invalid signature', 401

    # Process webhook...

Monitor Conditions

Define when your webhooks should fire

field

The metric to monitor

rate apr monthly_payment

operator

How to compare against your threshold

lt lte gt gte eq change_pct

Example Conditions

condition examples
// Alert when APR drops below 6.25%
{
  "field": "apr",
  "operator": "lt",
  "value": 6.25
}

// Alert when rate drops by more than 0.125% (12.5 basis points)
{
  "field": "rate",
  "operator": "change_pct",
  "value": -2.0  // -2% change in rate value
}

// Alert when monthly payment falls below $2,500
{
  "field": "monthly_payment",
  "operator": "lt",
  "value": 2500
}

// Combine multiple conditions (all must pass)
"conditions": [
  { "field": "apr", "operator": "lt", "value": 6.5 },
  { "field": "monthly_payment", "operator": "lt", "value": 3000 }
]

API Reference

Complete CRUD operations for monitor management

POST /v1/monitors Create a new monitor
GET /v1/monitors List all your monitors
GET /v1/monitors/:id Get a specific monitor
PATCH /v1/monitors/:id Update monitor settings
DELETE /v1/monitors/:id Delete a monitor
POST /v1/monitors/:id/simulate Test webhook delivery
View Full API Documentation

Best Practices

Recommendations for reliable webhook handling

Use Cooldown Periods

Set a cooldown (e.g., 60 minutes) to prevent alert fatigue from minor rate fluctuations. You'll only receive one webhook per cooldown period.

Implement Idempotency

Store the event_id from each webhook and check for duplicates before processing. This handles retries safely.

Respond Quickly

Acknowledge webhooks within 5 seconds by returning a 2xx status. Queue heavy processing for async execution.

Handle Retries

If your endpoint fails, we'll retry with exponential backoff. Design your handler to be idempotent and log failed attempts.

Always Verify Signatures

Check the HMAC-SHA256 signature on every webhook before processing. Never trust unverified payloads.

Log Everything

Log the full payload, headers, and your processing result. This makes debugging much easier when something goes wrong.

Ready to Get Started?

Create your first monitor and start receiving real-time rate alerts

Create API Key Back to Homepage