Overview
The Webhooks API allows you to configure endpoints that receive real-time HTTP notifications when events occur in your InstaView account. Webhooks provide an event-driven alternative to polling, enabling immediate integration with your systems.
Resource Structure
{
"id" : "550e8400-e29b-41d4-a716-446655440000" ,
"apiKeyId" : "api-key-uuid" ,
"companyId" : "company-uuid" ,
"url" : "https://api.example.com/webhooks/instaview" ,
"name" : "Production Webhook" ,
"description" : "Receives interview completion notifications for ATS integration" ,
"headers" : [
{ "name" : "Authorization" , "isMasked" : true },
{ "name" : "X-Custom-Header" , "isMasked" : false }
],
"events" : [ "interview.completed" , "analysis.completed" ],
"isActive" : true ,
"maxRetries" : 3 ,
"timeoutMs" : 30000 ,
"consecutiveFailures" : 0 ,
"circuitOpenedAt" : null ,
"createdAt" : "2024-01-15T10:30:00Z" ,
"updatedAt" : "2024-01-15T10:30:00Z"
}
Required Scopes
Operation Required Scope List webhooks read:webhooksGet webhook by ID read:webhooksCreate webhook write:webhooksUpdate webhook write:webhooksDelete webhook write:webhooksTest webhook (ping) write:webhooksReset circuit breaker write:webhooks
Creating Webhooks
Basic Webhook
curl -X POST https://api.instaview.sk/webhooks \
-H "Authorization: Bearer sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/webhooks/instaview",
"events": [3, 0]
}'
Comprehensive Webhook Configuration
const comprehensiveWebhook = {
// Required: Endpoint URL
url: 'https://api.example.com/webhooks/instaview' ,
// Required: Event types to subscribe to
events: [
0 , // analysis.completed
1 , // analysis.failed
2 , // ping (for testing)
3 , // interview.completed
4 , // interview.failed
5 // interview.started
],
// Optional: Human-readable name
name: 'Production Analysis Webhook' ,
// Optional: Description
description: 'Receives all interview and analysis events for our ATS integration' ,
// Optional: Company ID for per-company webhooks (ATS integrations)
companyId: 'company-uuid' , // If omitted, webhook receives events for all companies
// Optional: Custom headers for authentication
headers: [
{ name: 'Authorization' , value: 'Bearer your-internal-token' },
{ name: 'X-Source' , value: 'instaview' }
]
};
const response = await createWebhook ( comprehensiveWebhook );
// Store the signing secret securely!
await secretsManager . store ( 'INSTAVIEW_WEBHOOK_SECRET' , response . signingSecret );
Important : The signingSecret is only returned once during webhook
creation. Store it securely immediately. If you lose it, you must delete and
recreate the webhook.
Listing Webhooks
List All Webhooks
async function listWebhooks ( companyId ) {
const url = companyId
? `https://api.instaview.sk/webhooks?companyId= ${ companyId } `
: 'https://api.instaview.sk/webhooks' ;
const response = await fetch ( url , {
headers: { 'Authorization' : `Bearer ${ apiKey } ` }
});
const { data } = await response . json ();
return data ;
}
// List all webhooks (includes both company-specific and global webhooks)
const { data : webhooks , total } = await listWebhooks ();
console . log ( `Found ${ total } webhook configurations` );
// List webhooks for a specific company (excludes global webhooks)
// Only returns webhooks that have the specified companyId
const { data : companyWebhooks } = await listWebhooks ( 'company-uuid' );
console . log ( `Found ${ companyWebhooks . length } webhooks for company` );
Monitor Webhook Health
async function checkWebhookHealth () {
const { data : webhooks } = await listWebhooks ();
for ( const webhook of webhooks ) {
const status = {
name: webhook . name || webhook . id ,
active: webhook . isActive ,
failures: webhook . consecutiveFailures ,
circuitOpen: !! webhook . circuitOpenedAt
};
if ( webhook . consecutiveFailures > 0 ) {
console . warn ( `⚠️ ${ status . name } : ${ webhook . consecutiveFailures } failures` );
}
if ( webhook . circuitOpenedAt ) {
console . error ( `🔴 ${ status . name } : Circuit breaker open since ${ webhook . circuitOpenedAt } ` );
}
}
}
Updating Webhooks
Partial Update
async function updateWebhook ( webhookId , updates ) {
const response = await fetch (
`https://api.instaview.sk/webhooks/ ${ webhookId } ` ,
{
method: 'PATCH' ,
headers: {
'Authorization' : `Bearer ${ apiKey } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ( updates )
}
);
return await response . json ();
}
// Update URL
await updateWebhook ( 'webhook-uuid' , {
url: 'https://api.example.com/webhooks/instaview-v2'
});
// Add more events
await updateWebhook ( 'webhook-uuid' , {
events: [ 0 , 1 , 2 , 3 , 4 , 5 ] // All events
});
// Disable webhook
await updateWebhook ( 'webhook-uuid' , {
isActive: false
});
// Replace all custom headers
await updateWebhook ( 'webhook-uuid' , {
headers: [
{ name: 'Authorization' , value: 'Bearer new-token' },
{ name: 'X-Environment' , value: 'production' }
]
});
When updating headers, the entire headers array is replaced. Include all
headers you want to keep.
Event Types
Event Type Reference
ID Label Description Payload 0 analysis.completedAnalysis finished successfully Interview, call attempt, candidate, job IDs 1 analysis.failedAnalysis processing failed IDs + error message 2 pingTest event for connectivity Test message 3 interview.completedInterview finished with analysis Interview, call attempt, candidate, job IDs 4 interview.failedTechnical failure during interview IDs + error message 5 interview.startedInterview session began Interview, call attempt, candidate, job IDs
Event Selection Strategy
Minimal
Standard
Comprehensive
Subscribe only to completion events: {
events : [ 3 , 0 ] // interview.completed, analysis.completed
}
Use case : Simple integrations that only need final resultsInclude failure events for error handling: {
events : [ 0 , 1 , 3 , 4 ] // completed + failed events
}
Use case : Production integrations that need to handle errorsAll events for full visibility: {
events : [ 0 , 1 , 2 , 3 , 4 , 5 ] // All events
}
Use case : Real-time dashboards or detailed audit trails
Circuit Breaker
The circuit breaker protects both systems from cascading failures:
How It Works
Failures Accumulate
Each failed delivery increments consecutiveFailures
Circuit Opens
After threshold failures, circuitOpenedAt is set and deliveries stop
Deliveries Paused
No new deliveries are attempted while circuit is open
Manual Reset
Use the reset endpoint to re-enable after fixing issues
Monitoring Circuit State
async function monitorCircuitBreakers () {
const { data : webhooks } = await listWebhooks ();
for ( const webhook of webhooks ) {
if ( webhook . circuitOpenedAt ) {
console . error ( `Circuit OPEN: ${ webhook . name } ` );
console . error ( ` Opened at: ${ webhook . circuitOpenedAt } ` );
console . error ( ` Failures: ${ webhook . consecutiveFailures } ` );
// Alert your team
await sendAlert ({
type: 'webhook_circuit_open' ,
webhook: webhook . name ,
url: webhook . url ,
openedAt: webhook . circuitOpenedAt
});
}
}
}
Resetting Circuit Breaker
async function resetCircuitBreaker ( webhookId ) {
const response = await fetch (
`https://api.instaview.sk/webhooks/ ${ webhookId } /reset-circuit` ,
{
method: 'POST' ,
headers: { 'Authorization' : `Bearer ${ apiKey } ` }
}
);
const { data } = await response . json ();
console . log ( `Circuit reset. Active: ${ data . isActive } ` );
return data ;
}
Testing Webhooks
Send Test Ping
async function testWebhook ( webhookId ) {
const response = await fetch (
`https://api.instaview.sk/webhooks/ ${ webhookId } /test` ,
{
method: 'POST' ,
headers: { 'Authorization' : `Bearer ${ apiKey } ` }
}
);
const { data } = await response . json ();
if ( data . success ) {
console . log ( '✅ Webhook test successful' );
console . log ( ` Delivery ID: ${ data . deliveryId } ` );
console . log ( ` HTTP Status: ${ data . httpStatus } ` );
console . log ( ` Duration: ${ data . durationMs } ms` );
} else {
console . error ( '❌ Webhook test failed' );
console . error ( ` Message: ${ data . message } ` );
}
return data ;
}
The webhook must be subscribed to the ping event (type 2) for testing to
work.
Test Response Structure
{
"success" : true ,
"deliveryId" : "delivery-uuid" ,
"httpStatus" : 200 ,
"durationMs" : 156 ,
"message" : "Webhook test successful!"
}
Or on failure:
{
"success" : false ,
"message" : "Webhook test failed: Connection timeout"
}
Deleting Webhooks
Delete a Webhook
async function deleteWebhook ( webhookId ) {
const response = await fetch (
`https://api.instaview.sk/webhooks/ ${ webhookId } ` ,
{
method: 'DELETE' ,
headers: { 'Authorization' : `Bearer ${ apiKey } ` }
}
);
if ( response . status === 204 ) {
console . log ( 'Webhook deleted successfully' );
}
}
Deleting a webhook immediately stops all deliveries. Pending deliveries will
not be sent.
You can add custom headers for authentication or routing:
{
headers : [
// Authentication
{ name: 'Authorization' , value: 'Bearer your-token' },
{ name: 'X-API-Key' , value: 'your-api-key' },
// Custom headers
{ name: 'X-Source' , value: 'instaview' },
{ name: 'X-Environment' , value: 'production' }
]
}
The following headers are automatically encrypted at rest:
Authorization
X-API-Key
X-Secret
API-Key
When listing webhooks, these headers show isMasked: true and their values are hidden.
Maximum 50 custom headers per webhook
Header names: 1-255 characters
Header values: 1-4096 characters
Configuration Fields
The HTTPS endpoint URL to receive webhook notifications (max 2048 characters)
Array of event type IDs to subscribe to (0-5)
Human-readable name for the webhook (max 255 characters)
Description of the webhook’s purpose
Custom headers to include in requests (max 50 headers)
Whether the webhook is active (default: true, can be updated)
Response Fields
Unique identifier for the webhook configuration
The API key ID this webhook belongs to
Company ID this webhook is associated with. Null if webhook receives events for all companies (global webhook).
Maximum retry attempts for failed deliveries (system-configured)
Request timeout in milliseconds (system-configured, default 30000)
Current count of consecutive delivery failures
ISO 8601 timestamp when circuit breaker was triggered (null if closed)
HMAC signing secret (only returned on creation, hex-encoded)
Error Scenarios
{
"error" : {
"code" : "VALIDATION_ERROR" ,
"message" : "url must be a valid URL" ,
"details" : {
"field" : "url"
}
}
}
{
"error" : {
"code" : "VALIDATION_ERROR" ,
"message" : "Invalid event type" ,
"details" : {
"field" : "events" ,
"provided" : [ 99 ],
"allowed" : [ 0 , 1 , 2 , 3 , 4 , 5 ]
}
}
}
{
"error" : {
"code" : "RESOURCE_NOT_FOUND" ,
"message" : "Webhook configuration not found" ,
"details" : {
"resourceType" : "WebhookConfig" ,
"resourceId" : "invalid-uuid"
}
}
}
{
"error" : {
"code" : "RESOURCE_ACCESS_DENIED" ,
"message" : "Access denied to this webhook configuration"
}
}
{
"success" : false ,
"message" : "Webhook is not subscribed to ping events. Add event type 2 (ping) to the webhook configuration."
}
Common Patterns
Webhook Setup Flow
async function setupWebhook ( config ) {
// 1. Create webhook
const response = await createWebhook ({
url: config . url ,
name: config . name ,
events: [ 2 , 3 , 0 ], // ping, interview.completed, analysis.completed
headers: config . headers
});
// 2. Store signing secret securely
await secretsManager . store (
`WEBHOOK_SECRET_ ${ response . id } ` ,
response . signingSecret
);
// 3. Test connectivity
const testResult = await testWebhook ( response . id );
if ( ! testResult . success ) {
// Clean up if test fails
await deleteWebhook ( response . id );
throw new Error ( `Webhook test failed: ${ testResult . message } ` );
}
// 4. Remove ping event if not needed for production
await updateWebhook ( response . id , {
events: [ 3 , 0 ] // interview.completed, analysis.completed
});
return response ;
}
Health Check Automation
async function webhookHealthCheck () {
const { data : webhooks } = await listWebhooks ();
const issues = [];
for ( const webhook of webhooks ) {
// Check for failures
if ( webhook . consecutiveFailures > 5 ) {
issues . push ({
type: 'high_failures' ,
webhook: webhook . name ,
failures: webhook . consecutiveFailures
});
}
// Check for open circuits
if ( webhook . circuitOpenedAt ) {
issues . push ({
type: 'circuit_open' ,
webhook: webhook . name ,
openedAt: webhook . circuitOpenedAt
});
}
// Check for inactive webhooks
if ( ! webhook . isActive ) {
issues . push ({
type: 'inactive' ,
webhook: webhook . name
});
}
}
return issues ;
}
Best Practices
Use Descriptive Names Name webhooks clearly (e.g., “Production ATS Sync”, “Staging Test”)
Monitor Failures Set up alerts for consecutiveFailures > 0
Test Before Production Always test webhooks with ping before relying on them
Secure Secrets Use a secrets manager for signing secrets
Next Steps