Pricing Docs Blog About Contact Webhooks Sign In

Webhooks & Events

Know everything your agents spend —
in real time.

Push-based event delivery means you never poll. Every transaction, every policy decision, every budget threshold — delivered to your endpoint the moment it happens.

● At-least-once delivery ● Exponential retries ● HMAC-SHA256 signed
POST https://your-app.com/webhooks/reins
{ "id": "evt_a1b2c3d4e5f6", "event": "transaction.blocked", "created_at": "2026-05-14T14:23:11.000Z", "data": { "transaction_id": "txn_7x8y9z0a1b2c", "agent_id": "agt_f4e5d6c7b8a9", "agent_name": "research-bot-prod", "vendor": "openai", "model": "gpt-4o", "amount": 0.47, "currency": "USD", "reason": "Daily limit exceeded ($50.00)", "policy_id": "pol_3d4e5f6a7b8c", "blocked_at": "2026-05-14T14:23:11.000Z" } }

Event Catalog

11 event types. Full payloads.

Every event Reins fires, with trigger conditions, schemas, and example payloads. Click to expand.


Configuration

Set up in 5 minutes

From zero to receiving live events in five steps.

1

Create an endpoint

In the Reins dashboard, go to Settings → Webhooks → Add Endpoint. Enter your HTTPS URL. HTTP is rejected — all endpoints must use TLS. Your endpoint must return a 2xx within 30 seconds.

2

Configure event types

Select which events to subscribe to. You can toggle individual events on/off per endpoint. Start with transaction.blocked and budget.exceeded — the highest-signal events.

3

Set your signing secret

Reins generates an HMAC-SHA256 signing secret (whsec_...). Store it securely — you'll use it to verify every incoming payload. Never expose it in client-side code.

4

Test with sample payloads

Click Send Test Event in the dashboard to fire a synthetic payload at your endpoint. Check that your handler returns 200 and logs the event correctly. Use the Payload Builder below to craft custom test events.

5

Verify delivery logs

The Delivery Logs tab shows every attempt: status code, response time, retry count, and payload preview. Green means delivered. Red means your endpoint returned an error or timed out.


Security

Signature verification

Every webhook includes an X-Reins-Signature header. Verify it before processing.

import hmac import hashlib def verify_signature(payload, signature, secret): # Compute expected HMAC-SHA256 expected = hmac.new( secret.encode('utf-8'), payload.encode('utf-8'), hashlib.sha256 ).hexdigest() return hmac.compare_digest( f"sha256={expected}", signature ) # Usage in your handler: signature = request.headers['X-Reins-Signature'] if not verify_signature(request.data, signature, WEBHOOK_SECRET): return 'Unauthorized', 401
const crypto = require('crypto'); function verifySignature(payload, signature, secret) { const expected = crypto .createHmac('sha256', secret) .update(payload) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(`sha256=${expected}`), Buffer.from(signature) ); } // Usage in Express: const sig = req.headers['x-reins-signature']; if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET)) { return res.status(401).json({ error: 'Invalid signature' }); }

Replay attack prevention: Each payload includes a X-Reins-Timestamp header (Unix seconds). Reject any payload older than 5 minutes to prevent replay attacks. Compare Math.abs(Date.now()/1000 - timestamp) > 300.


Reliability

Delivery & retry logic

6 retry attempts with exponential backoff. Every delivery is logged.

1
ImmediateFirst attempt on event fire
2
+ 1 minuteFirst retry after initial failure
3
+ 5 minutesBackoff increases
4
+ 30 minutesEndpoint may be recovering
5
+ 2 hoursExtended backoff window
6
+ 24 hoursFinal attempt before dead letter
Dead letter queueInspect & replay from dashboard

Idempotency: Every event includes a unique id field. Store processed event IDs and skip duplicates. Retries send the same event ID, so your handler must be idempotent.


Try It

Payload builder

Select an event, fill in values, and see the generated webhook payload in real time.

Generated Payload

Integration

Receive webhooks in your stack

Full working examples for Python and Node.js. Copy, paste, deploy.

from flask import Flask, request, jsonify import hmac, hashlib, time app = Flask(__name__) WEBHOOK_SECRET = 'whsec_k7m2p9x4r1...' def verify(payload, sig, ts): # Reject replays older than 5 minutes if abs(time.time() - int(ts)) > 300: return False expected = hmac.new(WEBHOOK_SECRET.encode(), payload, hashlib.sha256).hexdigest() return hmac.compare_digest(f"sha256={expected}", sig) @app.route('/webhooks/reins', methods=['POST']) def handle_webhook(): sig = request.headers.get('X-Reins-Signature') ts = request.headers.get('X-Reins-Timestamp') if not verify(request.data, sig, ts): return jsonify({'error': 'Invalid signature'}), 401 event = request.json # Route by event type if event['event'] == 'transaction.blocked': handle_blocked(event['data']) elif event['event'] == 'budget.exceeded': alert_team(event['data']) return jsonify({'received': True}), 200
const express = require('express'); const crypto = require('crypto'); const app = express(); app.use(express.raw({ type: 'application/json' })); const SECRET = process.env.REINS_WEBHOOK_SECRET; function verify(payload, sig, ts) { // Reject replays older than 5 minutes if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; const expected = crypto .createHmac('sha256', SECRET) .update(payload).digest('hex'); return crypto.timingSafeEqual( Buffer.from(`sha256=${expected}`), Buffer.from(sig) ); } app.post('/webhooks/reins', (req, res) => { const sig = req.headers['x-reins-signature']; const ts = req.headers['x-reins-timestamp']; if (!verify(req.body, sig, ts)) return res.status(401).json({ error: 'Invalid signature' }); const event = JSON.parse(req.body); // Route by event type switch (event.event) { case 'transaction.blocked': handleBlocked(event.data); break; case 'budget.exceeded': alertTeam(event.data); break; } res.json({ received: true }); }); app.listen(3000);

Debugging

Delivery logs

Every delivery attempt is logged with status, response time, and payload preview. Inspect failures, replay events, and debug from the dashboard.

TimestampEventEndpointStatusRetriesLatency
2026-05-14 14:23:11transaction.blockedhttps://api.acme.co/webhooks2000142ms
2026-05-14 14:21:07transaction.approvedhttps://api.acme.co/webhooks200098ms
2026-05-14 14:18:55budget.exceededhttps://hooks.slack.com/abc2001210ms
2026-05-14 14:15:33alert.triggeredhttps://api.acme.co/webhooks500330012ms
2026-05-14 14:12:01agent.throttledhttps://ops.internal/reins408230000ms
2026-05-14 14:09:44transaction.approvedhttps://api.acme.co/webhooks200087ms

Failed deliveries show a Replay button. Click to re-send the exact same payload with a new delivery ID. All replays are logged separately.


Limits

Rate limits & quotas

Plan-level limits for webhook delivery. Enterprise plans are customizable.

LimitValue
Max webhooks per second100
Max payload size64 KB
Max concurrent connections50
Max endpoints per organization10
Delivery log retention30 days
Max retry attempts6
Timeout per delivery attempt30 seconds

Best Practices

Integration checklist

Ship a reliable webhook consumer with these patterns.

🔑

Use idempotency keys. Store processed event IDs. Retries re-send the same ID — skip duplicates to avoid double-processing.

Return 200 immediately. Acknowledge receipt, then process async in a background job. Slow handlers trigger unnecessary retries.

🔒

Verify every signature. Never skip HMAC verification, even in development. Unsigned payloads should be rejected with 401.

Monitor response times. If your endpoint consistently takes >5s, you risk timeouts. Offload heavy processing to a queue.

💾

Store raw payloads. Log the original JSON before parsing. When something breaks, you have the exact payload for debugging.

🚨

Alert on failures. Set up monitoring on your webhook endpoint. Consecutive 5xx responses mean you are missing events.

📂

Separate endpoints by concern. Use one endpoint for billing events, another for security alerts. Easier to debug and scale independently.

🔧

Test before going live. Use the Payload Builder above and the dashboard test-event button. Validate your handler end-to-end with synthetic data.


FAQ

Common questions


Related

Keep building