Webhooks
Receive real-time notifications about events in your AuxVault account.
Overview
Webhooks allow AuxVault to push real-time notifications to your server when important events occur, such as:
- ✅ Transactions approved/declined
- 💰 Refunds processed
- 🔁 Recurring payments processed
- 📄 Invoices paid
- ⚠️ Chargebacks received
- 🔄 Settlement completed
Instead of polling our API, webhooks push data to you instantly when events happen.
How Webhooks Work
- Event occurs (e.g., transaction approved)
- AuxVault sends HTTP POST to your webhook URL
- Your server responds with 200 OK
- If failed, AuxVault retries with exponential backoff
Setup
1. Create a Webhook Endpoint
Create an endpoint on your server to receive webhook notifications:
// Express.js example
app.post('/webhooks/auxvault', async (req, res) => {
const signature = req.headers['x-auxvault-signature'];
const payload = req.body;
// Verify signature
if (!verifySignature(payload, signature)) {
return res.status(401).send('Invalid signature');
}
// Process event
try {
await handleWebhookEvent(payload);
res.status(200).send('OK');
} catch (error) {
console.error('Webhook error:', error);
res.status(500).send('Error processing webhook');
}
});
2. Register Your Webhook URL
Endpoint: POST /api/v1/webhooks
curl -X POST https://api.auxvault.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-tenant-id" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yoursite.com/webhooks/auxvault",
"events": [
"transaction.approved",
"transaction.declined",
"refund.processed",
"subscription.payment_succeeded",
"subscription.payment_failed"
],
"description": "Production webhook"
}'
Response:
{
"success": true,
"data": {
"id": "webhook_abc123",
"url": "https://yoursite.com/webhooks/auxvault",
"events": ["transaction.approved", "transaction.declined", ...],
"secret": "whsec_1234567890abcdef...",
"status": "active",
"createdAt": "2026-01-28T00:00:00Z"
}
}
⚠️ Save the secret! You'll need it to verify webhook signatures.
Webhook Payload Structure
All webhooks follow this format:
{
"id": "evt_abc123",
"type": "transaction.approved",
"createdAt": "2026-01-28T12:34:56Z",
"data": {
// Event-specific data
},
"tenantId": "your-tenant-id",
"merchantId": "merchant-123"
}
Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique event ID |
type |
string | Event type (e.g., "transaction.approved") |
createdAt |
datetime | When the event occurred |
data |
object | Event-specific data |
tenantId |
string | Your tenant ID |
merchantId |
string | Associated merchant ID |
Event Types
Transaction Events
transaction.approved
Sent when a transaction is approved.
{
"id": "evt_001",
"type": "transaction.approved",
"createdAt": "2026-01-28T12:34:56Z",
"data": {
"transactionId": "txn_abc123",
"type": "sale",
"amount": 150.75,
"currency": "USD",
"authCode": "123456",
"card": {
"brand": "Visa",
"last4": "1111"
},
"customer": {
"email": "customer@example.com"
}
}
}
transaction.declined
Sent when a transaction is declined.
{
"id": "evt_002",
"type": "transaction.declined",
"createdAt": "2026-01-28T12:35:00Z",
"data": {
"transactionId": "txn_declined_123",
"type": "sale",
"amount": 50.00,
"declineCode": "insufficient_funds",
"declineReason": "Insufficient funds",
"card": {
"last4": "2222"
}
}
}
transaction.voided
Sent when a transaction is voided.
transaction.settled
Sent when a transaction is settled.
Refund Events
refund.processed
Sent when a refund is processed.
{
"id": "evt_003",
"type": "refund.processed",
"createdAt": "2026-01-28T13:00:00Z",
"data": {
"refundId": "ref_abc123",
"transactionId": "txn_original_123",
"amount": 150.75,
"status": "approved",
"reason": "Customer request"
}
}
refund.declined
Sent when a refund is declined.
Subscription Events
subscription.created
Sent when a subscription is created.
subscription.payment_succeeded
Sent when a recurring payment succeeds.
{
"id": "evt_004",
"type": "subscription.payment_succeeded",
"createdAt": "2026-01-28T14:00:00Z",
"data": {
"subscriptionId": "sub_abc123",
"transactionId": "txn_recurring_123",
"amount": 29.99,
"nextPaymentDate": "2026-02-28T00:00:00Z",
"customer": {
"email": "subscriber@example.com"
}
}
}
subscription.payment_failed
Sent when a recurring payment fails.
subscription.cancelled
Sent when a subscription is cancelled.
subscription.expired
Sent when a subscription expires.
Invoice Events
invoice.paid
Sent when an invoice is paid.
{
"id": "evt_005",
"type": "invoice.paid",
"createdAt": "2026-01-28T15:00:00Z",
"data": {
"invoiceId": "inv_abc123",
"transactionId": "txn_payment_123",
"amountPaid": 100.00,
"amountDue": 0.00,
"status": "paid",
"customer": {
"email": "customer@example.com"
}
}
}
invoice.partially_paid
Sent when a partial payment is made on an invoice.
invoice.overdue
Sent when an invoice becomes overdue.
Chargeback Events
chargeback.received
Sent when a chargeback is received.
{
"id": "evt_006",
"type": "chargeback.received",
"createdAt": "2026-01-28T16:00:00Z",
"data": {
"chargebackId": "cb_abc123",
"transactionId": "txn_disputed_123",
"amount": 150.75,
"reason": "fraudulent",
"dueDate": "2026-02-15T00:00:00Z"
}
}
chargeback.won
Sent when you win a chargeback.
chargeback.lost
Sent when you lose a chargeback.
Signature Verification
Always verify webhook signatures to ensure requests are from AuxVault.
How It Works
AuxVault signs each webhook with HMAC SHA-256 using your webhook secret. The signature is sent in the X-AuxVault-Signature header.
Node.js Example
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const computedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(computedSignature)
);
}
// In your webhook handler
app.post('/webhooks/auxvault', (req, res) => {
const signature = req.headers['x-auxvault-signature'];
const secret = process.env.WEBHOOK_SECRET;
if (!verifySignature(req.body, signature, secret)) {
return res.status(401).send('Invalid signature');
}
// Process webhook
res.status(200).send('OK');
});
Python Example
import hmac
import hashlib
import json
def verify_signature(payload, signature, secret):
computed_signature = hmac.new(
secret.encode(),
json.dumps(payload).encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, computed_signature)
# In your webhook handler
@app.route('/webhooks/auxvault', methods=['POST'])
def webhook():
signature = request.headers.get('X-AuxVault-Signature')
secret = os.environ['WEBHOOK_SECRET']
if not verify_signature(request.json, signature, secret):
return 'Invalid signature', 401
# Process webhook
return 'OK', 200
Handling Webhooks
Best Practices
- Respond quickly - Return 200 OK immediately
- Process asynchronously - Queue heavy processing
- Be idempotent - Handle duplicate events gracefully
- Verify signatures - Always verify before processing
- Log events - Keep audit trail of all webhooks
- Handle retries - Use event IDs to deduplicate
Example Handler
const processedEvents = new Set();
async function handleWebhookEvent(payload) {
const { id, type, data } = payload;
// Check if already processed (idempotency)
if (processedEvents.has(id)) {
console.log(`Event ${id} already processed`);
return;
}
// Mark as processed
processedEvents.add(id);
// Route to appropriate handler
switch (type) {
case 'transaction.approved':
await handleTransactionApproved(data);
break;
case 'transaction.declined':
await handleTransactionDeclined(data);
break;
case 'subscription.payment_failed':
await handleSubscriptionFailed(data);
break;
case 'invoice.paid':
await handleInvoicePaid(data);
break;
case 'chargeback.received':
await handleChargebackReceived(data);
break;
default:
console.log(`Unhandled event type: ${type}`);
}
}
async function handleTransactionApproved(data) {
// Send confirmation email
await sendEmail(data.customer.email, 'Payment Successful', ...);
// Update database
await db.updateOrderStatus(data.transactionId, 'paid');
// Trigger fulfillment
await fulfillOrder(data.transactionId);
}
Retry Logic
If your webhook endpoint fails (non-200 response), AuxVault will retry:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 12 hours |
| 7+ | 24 hours |
After 7 days of failed attempts, the webhook will be marked as failed.
Testing Webhooks
Local Development with ngrok
# Install ngrok
npm install -g ngrok
# Start your local server
node server.js # Running on localhost:3000
# Create tunnel
ngrok http 3000
# Use the ngrok URL as your webhook URL
# https://abc123.ngrok.io/webhooks/auxvault
Test Events
Trigger test events from the dashboard or API:
curl -X POST https://api.auxvault.com/api/v1/webhooks/webhook_abc123/test \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-tenant-id" \
-d '{"eventType": "transaction.approved"}'
Managing Webhooks
List Webhooks
curl https://api.auxvault.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-tenant-id"
Update Webhook
curl -X PUT https://api.auxvault.com/api/v1/webhooks/webhook_abc123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-tenant-id" \
-d '{
"events": ["transaction.approved", "transaction.declined"],
"status": "active"
}'
Delete Webhook
curl -X DELETE https://api.auxvault.com/api/v1/webhooks/webhook_abc123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-tenant-id"
Security Best Practices
✅ DO:
- Verify signatures on every request
- Use HTTPS for webhook URLs
- Implement rate limiting to prevent abuse
- Log all webhooks for audit trail
- Use idempotency to handle duplicates
- Respond quickly (< 5 seconds)
- Queue heavy processing for async handling
❌ DON'T:
- Don't skip signature verification
- Don't expose webhook URLs publicly
- Don't process synchronously if it takes > 5 seconds
- Don't retry on your end - AuxVault handles retries
- Don't trust the data without verification
Troubleshooting
Webhook Not Received
- Check webhook is active in dashboard
- Verify URL is correct and accessible
- Check firewall/IP whitelist settings
- Review webhook logs in dashboard
Signature Verification Fails
- Ensure you're using the correct secret
- Verify you're signing the raw JSON body
- Check for extra whitespace/formatting
- Make sure charset is UTF-8
Webhooks Timing Out
- Respond with 200 OK immediately
- Queue processing for async handling
- Don't wait for external services
- Keep response time < 5 seconds
Complete Event Reference
Next Steps
Need help? Contact support@auxvault.com