> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jobhive.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Events

JobHive uses webhooks to notify your application when events happen in real-time. Webhooks are HTTP POST requests sent to your configured endpoint URL whenever specific events occur.

## Webhook Configuration

Configure webhook endpoints in your JobHive dashboard:

1. Navigate to **Settings** → **Webhooks**
2. Add your endpoint URL (must be HTTPS)
3. Select which events to receive
4. Save your webhook secret for verification

<Warning>
  Webhook endpoints must respond with a 2xx status code within 10 seconds or the delivery will be considered failed.
</Warning>

## Event Types

JobHive sends webhooks for the following events:

<AccordionGroup>
  <Accordion title="interview.scheduled">
    Triggered when a new interview is scheduled

    ```json theme={null}
    {
      "event": "interview.scheduled",
      "data": {
        "id": "int_abc123def456",
        "candidate_email": "john.doe@example.com",
        "position": "Software Engineer",
        "scheduled_at": "2024-01-15T15:30:00Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="interview.started">
    Triggered when candidate joins and interview begins

    ```json theme={null}
    {
      "event": "interview.started", 
      "data": {
        "id": "int_abc123def456",
        "candidate": {
          "email": "john.doe@example.com",
          "joined_at": "2024-01-15T15:32:00Z"
        },
        "started_at": "2024-01-15T15:32:00Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="interview.completed">
    Triggered when interview is finished

    ```json theme={null}
    {
      "event": "interview.completed",
      "data": {
        "id": "int_abc123def456",
        "completed_at": "2024-01-15T16:14:00Z",
        "duration_minutes": 42,
        "results": {
          "overall_score": 78,
          "recommendation": "hire"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="interview.cancelled">
    Triggered when interview is cancelled

    ```json theme={null}
    {
      "event": "interview.cancelled",
      "data": {
        "id": "int_abc123def456", 
        "cancelled_at": "2024-01-15T14:45:00Z",
        "reason": "Position filled"
      }
    }
    ```
  </Accordion>

  <Accordion title="results.analyzed">
    Triggered when AI analysis is complete (a few seconds after interview\.completed)

    ```json theme={null}
    {
      "event": "results.analyzed",
      "data": {
        "interview_id": "int_abc123def456",
        "results": {
          "overall_score": 78,
          "technical_score": 82,
          "communication_score": 74,
          "recommendation": "hire",
          "strengths": ["React expertise", "Problem solving"],
          "areas_for_improvement": ["Testing", "Performance"]
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="candidate.no_show">
    Triggered when candidate doesn't join within 15 minutes of scheduled time

    ```json theme={null}
    {
      "event": "candidate.no_show",
      "data": {
        "interview_id": "int_abc123def456",
        "scheduled_at": "2024-01-15T15:30:00Z",
        "timeout_at": "2024-01-15T15:45:00Z"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Webhook Payload Structure

All webhooks follow this consistent structure:

```json theme={null}
{
  "event": "event_name",
  "data": {
    // Event-specific data
  },
  "webhook": {
    "id": "wh_abc123",
    "timestamp": "2024-01-15T15:30:00Z",
    "signature": "sha256=abc123..."
  },
  "account": {
    "id": "acc_xyz789",
    "name": "TechCorp Inc"
  }
}
```

## Webhook Security

### Signature Verification

JobHive signs each webhook with your webhook secret. Verify the signature to ensure authenticity:

<Tabs>
  <Tab title="Node.js">
    ```javascript theme={null}
    const crypto = require('crypto');

    function verifyWebhook(payload, signature, secret) {
      const expectedSignature = crypto
        .createHmac('sha256', secret)
        .update(payload, 'utf8')
        .digest('hex');
      
      const expectedHeader = `sha256=${expectedSignature}`;
      
      return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expectedHeader)
      );
    }

    app.post('/webhooks/jobhive', (req, res) => {
      const signature = req.headers['x-jobhive-signature'];
      const payload = JSON.stringify(req.body);
      
      if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
        return res.status(401).send('Invalid signature');
      }
      
      // Process webhook
      handleWebhook(req.body);
      res.status(200).send('OK');
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import hmac
    import hashlib

    def verify_webhook(payload, signature, secret):
        expected_signature = hmac.new(
            secret.encode('utf-8'),
            payload.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()
        
        expected_header = f"sha256={expected_signature}"
        
        return hmac.compare_digest(signature, expected_header)

    @app.route('/webhooks/jobhive', methods=['POST'])
    def handle_webhook():
        signature = request.headers.get('X-JobHive-Signature')
        payload = request.get_data(as_text=True)
        
        if not verify_webhook(payload, signature, os.environ['WEBHOOK_SECRET']):
            return 'Invalid signature', 401
        
        # Process webhook
        data = request.get_json()
        handle_webhook_event(data)
        return 'OK', 200
    ```
  </Tab>
</Tabs>

### Headers

JobHive includes these headers with every webhook:

| Header                | Description                              |
| --------------------- | ---------------------------------------- |
| `X-JobHive-Event`     | Event type (e.g., `interview.completed`) |
| `X-JobHive-Signature` | HMAC SHA-256 signature for verification  |
| `X-JobHive-Delivery`  | Unique delivery ID for tracking          |
| `X-JobHive-Timestamp` | Unix timestamp when webhook was sent     |
| `User-Agent`          | `JobHive-Webhooks/1.0`                   |

## Retry Logic

JobHive automatically retries failed webhook deliveries:

* **Retry Schedule**: 1min, 5min, 30min, 2hr, 12hr, 24hr
* **Failure Conditions**: Non-2xx response, timeout (>10s), connection error
* **Max Attempts**: 6 total attempts over \~37 hours
* **Exponential Backoff**: Delays increase between attempts

## Testing Webhooks

### Development with ngrok

Use ngrok to test webhooks locally:

```bash theme={null}
# Install ngrok
npm install -g ngrok

# Expose local server
ngrok http 3000

# Use the HTTPS URL in JobHive webhook settings
# Example: https://abc123.ngrok.io/webhooks/jobhive
```

### Test Events

Trigger test events from your JobHive dashboard:

1. Go to **Settings** → **Webhooks**
2. Select your webhook endpoint
3. Click **Send Test Event**
4. Choose event type and send

## Example Implementation

<Tabs>
  <Tab title="Express.js">
    ```javascript theme={null}
    const express = require('express');
    const app = express();

    app.use(express.json());

    app.post('/webhooks/jobhive', (req, res) => {
      const { event, data } = req.body;
      
      switch(event) {
        case 'interview.completed':
          // Send results to HR team
          notifyHRTeam(data);
          break;
          
        case 'interview.scheduled':
          // Add to calendar system
          addToCalendar(data);
          break;
          
        case 'candidate.no_show':
          // Log no-show for follow-up
          logNoShow(data);
          break;
      }
      
      res.status(200).send('OK');
    });

    function notifyHRTeam(interviewData) {
      // Implementation for HR notification
    }
    ```
  </Tab>

  <Tab title="Flask">
    ```python theme={null}
    from flask import Flask, request, jsonify

    app = Flask(__name__)

    @app.route('/webhooks/jobhive', methods=['POST'])
    def handle_webhook():
        data = request.get_json()
        event = data.get('event')
        event_data = data.get('data')
        
        if event == 'interview.completed':
            notify_hr_team(event_data)
        elif event == 'interview.scheduled':
            add_to_calendar(event_data)
        elif event == 'candidate.no_show':
            log_no_show(event_data)
        
        return jsonify({'status': 'success'}), 200

    def notify_hr_team(interview_data):
        # Implementation for HR notification
        pass
    ```
  </Tab>
</Tabs>

## Webhook Management

### List Webhook Endpoints

```bash theme={null}
curl -X GET "https://backend.jobhive.ai/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Create Webhook Endpoint

```bash theme={null}
curl -X POST "https://backend.jobhive.ai/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/jobhive",
    "events": ["interview.completed", "interview.scheduled"],
    "active": true
  }'
```

### View Webhook Deliveries

Monitor webhook delivery status and retry attempts in your JobHive dashboard under **Settings** → **Webhooks** → **Delivery Logs**.

<Note>
  Webhook endpoints must be HTTPS in production. HTTP endpoints are only allowed for development with `localhost` URLs.
</Note>
