Create a Webhook
Overview
Create a Webhook to receive automated notifications about events in your Ahrvo Network account. Webhooks enable real-time integrations by sending HTTP POST requests to your server when events occur.
Resource Access
- User Permissions: Admin users only
- Endpoint:
POST /webhooks
Arguments
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | HTTPS endpoint to receive webhook events |
| enabled_events | array | No | Specific events to subscribe to (default: all events) |
| authentication | object | No | Authentication credentials for your webhook endpoint |
Example Request (Basic Setup)
curl -X POST \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password \
-H 'Content-Type: application/json' \
-d '{
"url": "https://api.yourcompany.com/webhooks/finix"
}'
Example Request (With Specific Events)
curl -X POST \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password \
-H 'Content-Type: application/json' \
-d '{
"url": "https://api.yourcompany.com/webhooks/finix",
"enabled_events": [
{
"entity": "transfer",
"types": ["succeeded", "failed"]
},
{
"entity": "merchant",
"types": ["created", "verification.succeeded", "verification.failed"]
},
{
"entity": "payment_instrument",
"types": ["created", "verification.succeeded"]
}
]
}'
Example Request (With Bearer Authentication)
curl -X POST \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password \
-H 'Content-Type: application/json' \
-d '{
"url": "https://api.yourcompany.com/webhooks/finix",
"authentication": {
"type": "BEARER",
"bearer": {
"token": "your-secret-bearer-token-here"
}
},
"enabled_events": [
{
"entity": "transfer",
"types": ["succeeded", "failed"]
}
]
}'
Example Request (With Basic Authentication)
curl -X POST \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password \
-H 'Content-Type: application/json' \
-d '{
"url": "https://api.yourcompany.com/webhooks/finix",
"authentication": {
"type": "BASIC",
"basic": {
"username": "webhook_user",
"password": "secure_webhook_password"
}
}
}'
Example Response
{
"id": "WHwebhook123",
"created_at": "2023-12-10T20:00:00Z",
"updated_at": "2023-12-10T20:00:00Z",
"url": "https://api.yourcompany.com/webhooks/finix",
"enabled": true,
"authentication": {
"type": "BEARER",
"bearer": {
"token": "your-secret-bearer-token-here"
}
},
"enabled_events": [
{
"entity": "transfer",
"types": ["succeeded", "failed"]
}
],
"secret_signing_key": "whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/webhooks/WHwebhook123"
}
}
}
Additional Information
- URL: Your webhook endpoint
- Must be HTTPS (not HTTP)
- Publicly accessible
- Must respond with 200-299 status
- Timeout: 30 seconds
- Finix sends POST requests to this URL
- Enabled Events: Filter which events to receive
- Omit to receive ALL events (not recommended for production)
- Specify array of entity/types combinations
- Reduces noise
- Improves performance
- Common entities: transfer, merchant, payment_instrument, settlement
- Common types: created, succeeded, failed, updated
- Authentication: Secure your webhook endpoint
- NONE: No authentication (not recommended)
- BASIC: HTTP Basic Auth
- Send username and password
- Finix includes in Authorization header
- BEARER: Bearer token
- Send token
- Finix includes as
Authorization: Bearer {token}
- Choose based on your endpoint security
- Secret Signing Key: Verify webhook authenticity
- Returned ONLY on creation
- Use to verify webhook signatures
- HMAC-SHA256 signature
- Prevents spoofed webhooks
- Store securely
- Cannot retrieve later
- Enabled: Active status
- Created as
enabled: trueby default - Can disable via PUT /webhooks/{id}
- Disabled webhooks don't send events
- Use to pause temporarily
- Created as
- Automatic Retry: Failed deliveries
- Retries up to 3 times
- Exponential backoff
- 1st retry: 1 minute
- 2nd retry: 5 minutes
- 3rd retry: 15 minutes
- After 3 failures: Stops sending
- Must fix endpoint and re-enable
- Event Format: POST request body
{
"type": "transfer.succeeded",
"id": "EVevent123",
"created_at": "2023-12-10T20:00:00Z",
"data": {
"id": "TRtransfer123",
"state": "SUCCEEDED",
"amount": 10000,
// ... full resource object
}
}
Available Events
Transfer Events
transfer.created: Transfer createdtransfer.succeeded: Transfer completed successfullytransfer.failed: Transfer failedtransfer.updated: Transfer metadata updated
Merchant Events
merchant.created: Merchant createdmerchant.updated: Merchant info updatedmerchant.verification.succeeded: Merchant verifiedmerchant.verification.failed: Merchant verification failed
Payment Instrument Events
payment_instrument.created: Card/bank addedpayment_instrument.updated: Card/bank updatedpayment_instrument.verification.succeeded: Card/bank verifiedpayment_instrument.verification.failed: Card/bank verification failed
Settlement Events
settlement.created: Settlement batch createdsettlement.approved: Settlement approved for payoutsettlement.funding_transfer.created: Payout initiatedsettlement.funding_transfer.succeeded: Payout completedsettlement.funding_transfer.failed: Payout failed
Subscription Events
subscription.created: Subscription startedsubscription.updated: Subscription modifiedsubscription.canceled: Subscription canceledsubscription.expired: Subscription ended
Dispute Events
dispute.created: Chargeback receiveddispute.updated: Dispute status changeddispute.won: Dispute wondispute.lost: Dispute lost
Use Cases
Production Payment Notifications
# Subscribe to critical payment events
curl -X POST \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password \
-H 'Content-Type: application/json' \
-d '{
"url": "https://api.yourcompany.com/webhooks/payments",
"authentication": {
"type": "BEARER",
"bearer": {
"token": "prod_webhook_secret_token_xyz"
}
},
"enabled_events": [
{
"entity": "transfer",
"types": ["succeeded", "failed"]
}
]
}'
- Get notified when payments complete
- No polling required
- Real-time order fulfillment
- Immediate failure handling
Merchant Onboarding Webhook
# Monitor merchant verification
curl -X POST \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password \
-H 'Content-Type: application/json' \
-d '{
"url": "https://api.yourcompany.com/webhooks/merchants",
"enabled_events": [
{
"entity": "merchant",
"types": ["verification.succeeded", "verification.failed"]
}
]
}'
- Know when merchant is verified
- Trigger welcome emails
- Enable merchant dashboard access
- Handle verification failures
Settlement Notifications
# Track payouts to merchants
curl -X POST \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password \
-H 'Content-Type: application/json' \
-d '{
"url": "https://api.yourcompany.com/webhooks/settlements",
"enabled_events": [
{
"entity": "settlement",
"types": ["funding_transfer.succeeded", "funding_transfer.failed"]
}
]
}'
- Notify merchants of payouts
- Update accounting systems
- Handle failed payouts
- Reconciliation automation
Development/Testing Webhook
# Test webhook with all events
curl -X POST \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password \
-H 'Content-Type: application/json' \
-d '{
"url": "https://webhook.site/your-unique-url",
"enabled_events": []
}'
- Use webhook.site or similar for testing
- Receive all events to understand structure
- Debug webhook handling
- Validate event data
Best Practices
-
Use HTTPS: Required for security
- Not HTTP
- Valid SSL certificate
- No self-signed certs in production
- Publicly accessible
-
Store Signing Key: Save immediately
// On webhook creation
const webhook = await createWebhook({
url: 'https://api.yourcompany.com/webhooks'
});
// IMPORTANT: Store this securely
await storeSecurely({
webhook_id: webhook.id,
signing_key: webhook.secret_signing_key
});
// Cannot retrieve later! -
Filter Events: Don't subscribe to everything
- Only events you need
- Reduces load on your server
- Easier to process
- Better performance
- Example: Only transfer.succeeded for order fulfillment
-
Authenticate Endpoint: Secure your webhook
- Use BEARER or BASIC authentication
- Don't expose public webhook
- Prevents unauthorized access
- Verify signing key in your handler
-
Idempotency: Handle duplicate events
// Webhook handler
app.post('/webhooks/finix', async (req, res) => {
const event = req.body;
const eventId = event.id;
// Check if already processed
const exists = await db.events.findOne({ id: eventId });
if (exists) {
console.log('Duplicate event, ignoring');
return res.status(200).send('OK');
}
// Process event
await processEvent(event);
// Store event ID
await db.events.insert({ id: eventId, processed_at: new Date() });
res.status(200).send('OK');
}); -
Quick Response: Return 200 immediately
app.post('/webhooks/finix', async (req, res) => {
const event = req.body;
// Respond immediately
res.status(200).send('OK');
// Process asynchronously
processWebhookAsync(event).catch(err => {
console.error('Webhook processing error:', err);
});
}); -
Verify Signature: Prevent spoofing
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(JSON.stringify(payload));
const calculated = hmac.digest('hex');
return calculated === signature;
}
app.post('/webhooks/finix', (req, res) => {
const signature = req.headers['x-finix-signature'];
const isValid = verifyWebhookSignature(
req.body,
signature,
WEBHOOK_SIGNING_KEY
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process webhook
res.status(200).send('OK');
}); -
Error Handling: Log and alert
- Log all webhook processing errors
- Alert on repeated failures
- Monitor webhook delivery success rate
- Set up dead letter queue for failed events
Common Workflows
Complete Webhook Setup
- Create webhook with your URL
- Store secret_signing_key securely
- Implement webhook handler endpoint
- Verify signature in handler
- Return 200 OK quickly
- Process events asynchronously
- Test with test events
- Monitor for failures
- Deploy to production
Webhook Handler Implementation
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Webhook endpoint
app.post('/webhooks/finix', async (req, res) => {
try {
// 1. Verify signature
const signature = req.headers['x-finix-signature'];
if (!verifySignature(req.body, signature)) {
return res.status(401).send('Invalid signature');
}
// 2. Check idempotency
const eventId = req.body.id;
if (await isProcessed(eventId)) {
return res.status(200).send('OK');
}
// 3. Respond immediately
res.status(200).send('OK');
// 4. Process asynchronously
const event = req.body;
switch (event.type) {
case 'transfer.succeeded':
await handleTransferSucceeded(event.data);
break;
case 'transfer.failed':
await handleTransferFailed(event.data);
break;
case 'merchant.verification.succeeded':
await handleMerchantVerified(event.data);
break;
default:
console.log('Unhandled event type:', event.type);
}
// 5. Mark as processed
await markProcessed(eventId);
} catch (error) {
console.error('Webhook error:', error);
// Already sent 200, log error for investigation
}
});
async function handleTransferSucceeded(transfer) {
// Fulfill order
await fulfillOrder(transfer.tags.order_id);
// Send confirmation email
await sendConfirmation(transfer.tags.customer_email);
// Update database
await db.orders.update(
{ id: transfer.tags.order_id },
{ status: 'paid', payment_id: transfer.id }
);
}
app.listen(3000);
Testing Webhooks Locally
# Use ngrok to expose local server
ngrok http 3000
# Create webhook with ngrok URL
curl -X POST \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password \
-H 'Content-Type: application/json' \
-d '{
"url": "https://abc123.ngrok.io/webhooks/finix"
}'
# Trigger events in test environment
# Watch webhook calls in real-time
Security Considerations
-
HTTPS Only: Never use HTTP
- SSL/TLS encryption required
- Valid certificate
- No self-signed in production
-
Signature Verification: Always verify
- Use secret_signing_key
- Prevent spoofed webhooks
- Reject invalid signatures
- HMAC-SHA256 verification
-
Authentication: Secure your endpoint
- Bearer token recommended
- Strong, random tokens
- Rotate periodically
- Don't commit to source control
-
Signing Key Storage: Keep secure
- Store in secrets manager
- Environment variables
- Never commit to git
- Rotate if compromised
-
Rate Limiting: Protect your endpoint
- Limit requests per minute
- Prevent abuse
- Use queue for processing
- Handle bursts gracefully
Troubleshooting
Webhook Not Receiving Events
- Check URL is publicly accessible
- Verify HTTPS with valid cert
- Ensure endpoint returns 200
- Check enabled_events filter
- Verify webhook is enabled
- Check firewall rules
Events Failing Delivery
- Check endpoint response time (< 30s)
- Verify 200 status code
- Check server logs for errors
- Test endpoint manually
- Review authentication
Duplicate Events
- Implement idempotency
- Store event IDs
- Check for processing
- Ignore duplicates
Missing Signing Key
- Only shown on creation
- Cannot retrieve later
- Create new webhook
- Update your handler
Related Endpoints
- GET /webhooks: List all webhooks
- GET /webhooks/{id}: Fetch webhook details
- PUT /webhooks/{id}: Update webhook (enable/disable, change URL)