List Webhooks
Overview
Retrieve a list of all Webhooks configured for your account. View webhook URLs, enabled events, and authentication settings.
Resource Access
- User Permissions: Admin users only
- Endpoint:
GET /webhooks
Arguments
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Number of results per page (default: 10, max: 100) |
Example Request
curl -X GET \
'https://api.ahrvo.network/payments/na/webhooks?limit=20' \
-u username:password
Example Response
{
"_embedded": {
"webhooks": [
{
"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"
}
},
"enabled_events": [
{
"entity": "transfer",
"types": ["succeeded", "failed"]
}
],
"secret_signing_key": null,
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/webhooks/WHwebhook123"
}
}
},
{
"id": "WHwebhook124",
"created_at": "2023-12-09T15:30:00Z",
"updated_at": "2023-12-11T10:00:00Z",
"url": "https://api.yourcompany.com/webhooks/settlements",
"enabled": false,
"authentication": {
"type": "BASIC",
"basic": {
"username": "webhook_user",
"password": "secure_password"
}
},
"enabled_events": [
{
"entity": "settlement",
"types": ["funding_transfer.succeeded", "funding_transfer.failed"]
}
],
"secret_signing_key": null,
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/webhooks/WHwebhook124"
}
}
},
{
"id": "WHwebhook125",
"created_at": "2023-12-08T10:00:00Z",
"updated_at": "2023-12-08T10:00:00Z",
"url": "https://webhook.site/test-url",
"enabled": true,
"authentication": {
"type": "NONE"
},
"enabled_events": [],
"secret_signing_key": null,
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/webhooks/WHwebhook125"
}
}
}
]
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/webhooks?limit=20"
}
},
"page": {
"limit": 20,
"offset": 0,
"count": 3
}
}
Additional Information
- No Signing Key: Not returned in list
secret_signing_keyis always null- Only returned on creation
- Cannot retrieve after creation
- Security measure
- Must store on creation
- Enabled Status: Active webhooks
true: Webhook sends eventsfalse: Webhook disabled, no events sent- Use to pause temporarily
- Update via PUT /webhooks/{id}
- URL: Webhook endpoint
- Your server URL
- Must be HTTPS
- Where events are sent
- Can be updated
- Authentication: Endpoint security
- NONE: No authentication
- BASIC: HTTP Basic Auth (username/password shown)
- BEARER: Bearer token (token shown)
- Credentials visible in list (secure your API access)
- Enabled Events: Event filters
- Empty array = all events
- Specific entities/types = filtered
- Reduces noise
- Shows what events this webhook receives
- Created/Updated: Timestamps
- ISO 8601 format
- UTC timezone
created_at: Never changesupdated_at: Last modification
- Multiple Webhooks: Allowed
- Different URLs for different purposes
- Separate dev/staging/production
- Event-specific webhooks
- Redundancy/failover
- No Pagination Needed: Usually
- Most accounts have few webhooks
- Typically < 10 webhooks
- Pagination available if needed
Use Cases
Audit Active Webhooks
# List all webhooks to see what's configured
curl -X GET \
'https://api.ahrvo.network/payments/na/webhooks' \
-u username:password
- See all webhook endpoints
- Verify URLs are correct
- Check which are enabled
- Review event subscriptions
Find Disabled Webhooks
// List webhooks and find disabled ones
async function findDisabledWebhooks() {
const response = await listWebhooks({ limit: 100 });
const webhooks = response._embedded.webhooks;
const disabled = webhooks.filter(w => !w.enabled);
console.log(`Disabled webhooks: ${disabled.length}`);
disabled.forEach(w => {
console.log(`- ${w.id}: ${w.url}`);
console.log(` Disabled on: ${w.updated_at}`);
console.log(` Events: ${JSON.stringify(w.enabled_events)}`);
});
return disabled;
}
Verify Production Webhooks
// Check production webhooks are active
async function verifyProductionWebhooks() {
const response = await listWebhooks({ limit: 100 });
const webhooks = response._embedded.webhooks;
const production = webhooks.filter(w =>
w.url.includes('api.yourcompany.com') ||
w.url.includes('production')
);
console.log('Production Webhooks:');
production.forEach(w => {
const status = w.enabled ? '✓' : '✗';
console.log(`${status} ${w.url}`);
console.log(` Enabled: ${w.enabled}`);
console.log(` Auth: ${w.authentication.type}`);
console.log(` Events: ${w.enabled_events.length > 0 ? 'Filtered' : 'All'}`);
});
// Alert if any production webhook is disabled
const disabledProd = production.filter(w => !w.enabled);
if (disabledProd.length > 0) {
console.error(`⚠ ${disabledProd.length} production webhooks disabled!`);
}
}
Export Webhook Configuration
// Export all webhook configs for documentation
async function exportWebhookConfig() {
const response = await listWebhooks({ limit: 100 });
const webhooks = response._embedded.webhooks;
const config = webhooks.map(w => ({
id: w.id,
url: w.url,
enabled: w.enabled,
authentication: w.authentication.type,
events: w.enabled_events.length === 0
? 'All events'
: w.enabled_events.map(e => `${e.entity}:${e.types.join(',')}`).join('; '),
created: w.created_at
}));
// Export to CSV
const csv = [
'ID,URL,Enabled,Auth Type,Events,Created',
...config.map(c =>
`${c.id},"${c.url}",${c.enabled},${c.authentication},"${c.events}",${c.created}`
)
].join('\n');
return csv;
}
Find Test Webhooks
// Find webhooks pointing to test services
async function findTestWebhooks() {
const response = await listWebhooks({ limit: 100 });
const webhooks = response._embedded.webhooks;
const testUrls = [
'webhook.site',
'requestbin',
'ngrok',
'localhost',
'test',
'staging'
];
const testWebhooks = webhooks.filter(w =>
testUrls.some(test => w.url.includes(test))
);
console.log(`Test webhooks found: ${testWebhooks.length}`);
testWebhooks.forEach(w => {
console.log(`- ${w.id}: ${w.url}`);
});
return testWebhooks;
}
Group by Event Type
// See which events have webhook coverage
async function groupWebhooksByEvents() {
const response = await listWebhooks({ limit: 100 });
const webhooks = response._embedded.webhooks.filter(w => w.enabled);
const eventCoverage = {};
webhooks.forEach(webhook => {
if (webhook.enabled_events.length === 0) {
console.log(`${webhook.id}: Receives ALL events`);
} else {
webhook.enabled_events.forEach(event => {
event.types.forEach(type => {
const key = `${event.entity}.${type}`;
if (!eventCoverage[key]) {
eventCoverage[key] = [];
}
eventCoverage[key].push(webhook.url);
});
});
}
});
console.log('\nEvent Coverage:');
Object.entries(eventCoverage).forEach(([event, urls]) => {
console.log(`${event}:`);
urls.forEach(url => console.log(` - ${url}`));
});
}
Best Practices
-
Regular Audits: Review periodically
- Monthly reviews recommended
- Check for old/unused webhooks
- Verify URLs still valid
- Confirm authentication secure
- Remove test webhooks from production
-
Document Webhooks: Keep inventory
- What each webhook is for
- Who owns each webhook
- When created and why
- Environment (dev/staging/prod)
- Update documentation when changes made
-
Security Check: Verify authentication
// Check all webhooks have authentication
async function securityAudit() {
const response = await listWebhooks({ limit: 100 });
const webhooks = response._embedded.webhooks;
const noAuth = webhooks.filter(w =>
w.authentication.type === 'NONE'
);
if (noAuth.length > 0) {
console.warn(`⚠ ${noAuth.length} webhooks without authentication!`);
noAuth.forEach(w => {
console.log(` - ${w.url}`);
});
} else {
console.log('✓ All webhooks have authentication');
}
} -
Cleanup: Remove unused webhooks
- Delete old test webhooks
- Remove disabled webhooks (if no longer needed)
- Consolidate similar webhooks
- Reduces clutter
-
Monitoring: Track webhook status
- Alert if production webhook disabled
- Monitor enabled count
- Track authentication changes
- Audit log reviews
Common Workflows
Production Webhook Verification
- List all webhooks
- Filter for production URLs
- Verify all are enabled
- Check authentication is BEARER or BASIC
- Confirm event filters are correct
- Alert if any issues found
Cleanup Workflow
async function cleanupWebhooks() {
const response = await listWebhooks({ limit: 100 });
const webhooks = response._embedded.webhooks;
// Find test webhooks
const testWebhooks = webhooks.filter(w =>
w.url.includes('webhook.site') ||
w.url.includes('ngrok') ||
w.url.includes('localhost')
);
console.log(`Found ${testWebhooks.length} test webhooks`);
// Find disabled webhooks older than 30 days
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const oldDisabled = webhooks.filter(w => {
return !w.enabled &&
new Date(w.updated_at) < thirtyDaysAgo;
});
console.log(`Found ${oldDisabled.length} webhooks disabled >30 days`);
// Review before deletion
const toDelete = [...testWebhooks, ...oldDisabled];
console.log('\nWebhooks to delete:');
toDelete.forEach(w => {
console.log(`- ${w.id}: ${w.url} (${w.enabled ? 'enabled' : 'disabled'})`);
});
return toDelete;
}
Migration Verification
// After migrating to new webhook endpoint
async function verifyMigration(oldUrl, newUrl) {
const response = await listWebhooks({ limit: 100 });
const webhooks = response._embedded.webhooks;
const oldWebhook = webhooks.find(w => w.url === oldUrl);
const newWebhook = webhooks.find(w => w.url === newUrl);
console.log('Migration Status:');
console.log(`Old webhook (${oldUrl}):`, oldWebhook ?
`Found, ${oldWebhook.enabled ? 'enabled' : 'disabled'}` :
'Not found'
);
console.log(`New webhook (${newUrl}):`, newWebhook ?
`Found, ${newWebhook.enabled ? 'enabled' : 'disabled'}` :
'Not found'
);
// Verify new webhook is enabled
if (newWebhook && newWebhook.enabled) {
console.log('✓ New webhook is active');
// Optionally disable old webhook
if (oldWebhook && oldWebhook.enabled) {
console.log('Note: Old webhook still enabled');
}
} else {
console.error('✗ New webhook not active!');
}
}
Compare Environments
// Compare webhooks across environments
async function compareEnvironments() {
// Assume different API credentials per environment
const prodWebhooks = await listWebhooks({
credentials: prodCreds
});
const stagingWebhooks = await listWebhooks({
credentials: stagingCreds
});
console.log('Production webhooks:', prodWebhooks._embedded.webhooks.length);
console.log('Staging webhooks:', stagingWebhooks._embedded.webhooks.length);
// Compare event subscriptions
const prodEvents = new Set();
const stagingEvents = new Set();
prodWebhooks._embedded.webhooks.forEach(w => {
w.enabled_events.forEach(e => {
e.types.forEach(t => prodEvents.add(`${e.entity}.${t}`));
});
});
stagingWebhooks._embedded.webhooks.forEach(w => {
w.enabled_events.forEach(e => {
e.types.forEach(t => stagingEvents.add(`${e.entity}.${t}`));
});
});
console.log('Event coverage comparison:');
console.log('Prod events:', Array.from(prodEvents).sort());
console.log('Staging events:', Array.from(stagingEvents).sort());
}
Security Considerations
-
API Access: Limit who can list
- Only admin users
- Shows all webhook URLs
- Exposes authentication credentials
- Audit access logs
-
Credentials Visible: Be aware
- Basic auth username/password shown
- Bearer tokens shown
- Anyone with API access can see
- Use strong webhook authentication
- Rotate credentials periodically
-
Signing Keys Not Shown: By design
secret_signing_keyalways null in list- Cannot retrieve after creation
- Security feature
- Create new webhook if lost
-
Production URLs: Protect
- Don't expose internal URLs
- Use API gateway if possible
- Firewall webhook endpoints
- Monitor for unauthorized access
Troubleshooting
No Webhooks Returned
- Check you have created webhooks
- Verify API credentials
- Confirm account/environment
- May be different Application
Missing Expected Webhook
- Check webhook ID
- May have been deleted
- Verify URL/environment
- Check different Application
Signing Key is Null
- By design for security
- Only shown on creation
- Cannot retrieve later
- Must create new webhook if lost
Related Endpoints
- POST /webhooks: Create new webhook
- GET /webhooks/{id}: Fetch specific webhook
- PUT /webhooks/{id}: Update webhook (enable/disable, change URL)