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

# Project Completion Webhooks

Receive real-time notifications when project generation is complete.

## Overview

Webhooks provide instant notifications when your projects finish generating, eliminating the need for constant polling. When you include a `webhookUrl` in your project creation request, InitRepo will send a POST request to that URL when generation completes.

## Setup

Include a `webhookUrl` in your project creation request:

<RequestExample>
  ```json theme={null}
  {
    "projectIdea": "Build a task management app",
    "projectType": "web",
    "webhookUrl": "https://your-app.com/webhooks/initrepo"
  }
  ```
</RequestExample>

## Webhook Payload

<ResponseExample>
  ```json theme={null}
  {
    "event": "project.completed",
    "timestamp": "2024-01-15T10:45:00Z",
    "data": {
      "projectId": "550e8400-e29b-41d4-a716-446655440000",
      "project": {
        "name": "TaskFlow App",
        "status": "completed",
        "creditsConsumed": 15000,
        "createdAt": "2024-01-15T10:30:00Z",
        "completedAt": "2024-01-15T10:45:00Z"
      }
    }
  }
  ```
</ResponseExample>

## Webhook Headers

```http theme={null}
Content-Type: application/json
X-InitRepo-Event: project.completed
User-Agent: InitRepo-Webhooks/1.0
X-InitRepo-Signature: sha256=abc123def456...
X-InitRepo-Timestamp: 1699123456
X-Request-ID: req_1234567890
API-Version: v1.0
```

## Payload Fields

<ResponseField name="event" type="string">
  Always "project.completed" for completion notifications.
</ResponseField>

<ResponseField name="timestamp" type="string">
  ISO 8601 timestamp when the webhook was sent.
</ResponseField>

<ResponseField name="data.projectId" type="string">
  Unique identifier of the completed project.
</ResponseField>

<ResponseField name="data.project.name" type="string">
  Generated project name.
</ResponseField>

<ResponseField name="data.project.status" type="string">
  Always "completed" for success notifications.
</ResponseField>

<ResponseField name="data.project.creditsConsumed" type="integer">
  Total credits consumed for this project.
</ResponseField>

<ResponseField name="data.project.createdAt" type="string">
  ISO 8601 timestamp when the project was created.
</ResponseField>

<ResponseField name="data.project.completedAt" type="string">
  ISO 8601 timestamp when the project completed.
</ResponseField>

## Security Requirements

### HTTPS Only

* Webhook URLs must use HTTPS protocol
* HTTP URLs will be rejected during project creation
* TLS 1.2 or higher required

### No Private IPs

* Private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are blocked
* Localhost addresses (127.0.0.0/8) are not allowed
* Link-local addresses (169.254.0.0/16) are blocked

### Webhook Signature Verification (Required)

InitRepo signs all webhook payloads with HMAC-SHA256 using your webhook secret. **Always verify signatures** to ensure authenticity.

<Warning>
  **Security Notice**

  Webhook signature verification is **required** for security. Unsigned webhooks should be rejected to prevent spoofing attacks.
</Warning>

#### Getting Your Webhook Secret

Your webhook secret is available in your InitRepo dashboard under API Keys. Each API key has its own unique webhook secret.

#### Signature Verification Process

```javascript theme={null}
import crypto from 'crypto';

function verifyWebhookSignature(payload, signature, secret) {
  // Create HMAC hash of the payload
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload, 'utf8');
  const expectedSignature = `sha256=${hmac.digest('hex')}`;
  
  // Use timingSafeEqual to prevent timing attacks
  const sigBuffer = Buffer.from(signature, 'utf8');
  const expectedBuffer = Buffer.from(expectedSignature, 'utf8');
  
  return sigBuffer.length === expectedBuffer.length && 
         crypto.timingSafeEqual(sigBuffer, expectedBuffer);
}

// Example webhook handler with signature verification
app.post('/webhooks/initrepo', (req, res) => {
  const signature = req.headers['x-initrepo-signature'];
  const payload = JSON.stringify(req.body);
  const secret = process.env.INITREPO_WEBHOOK_SECRET;

  // Verify signature
  if (!verifyWebhookSignature(payload, signature, secret)) {
    console.error('Invalid webhook signature');
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const { event, data } = req.body;

  // Validate event type
  if (event !== 'project.completed') {
    return res.status(400).json({ error: 'Invalid event type' });
  }

  // Process the webhook
  console.log('Project completed:', data.projectId);
  res.status(200).json({ received: true });
});
```

#### Python Example

```python theme={null}
import hmac
import hashlib

def verify_webhook_signature(payload, signature, secret):
    """Verify webhook signature using HMAC-SHA256"""
    expected = hmac.new(
        secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    expected_sig = f"sha256={expected}"
    
    return hmac.compare_digest(expected_sig, signature)

@app.route('/webhooks/initrepo', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-InitRepo-Signature')
    payload = request.get_data(as_text=True)
    secret = os.environ['INITREPO_WEBHOOK_SECRET']
    
    if not verify_webhook_signature(payload, signature, secret):
        return jsonify({'error': 'Invalid signature'}), 401
    
    data = request.json
    # Process webhook...
    return jsonify({'received': True}), 200
```

### Additional Security Measures

#### Rate Limiting Protection

* Implement rate limiting on your webhook endpoints
* InitRepo includes automatic retry protection against abuse
* Maximum 6 delivery attempts per webhook with exponential backoff

#### Request Validation

```javascript theme={null}
app.post('/webhooks/initrepo', (req, res) => {
  // Verify content type
  if (req.headers['content-type'] !== 'application/json') {
    return res.status(400).json({ error: 'Invalid content type' });
  }

  // Verify user agent
  const userAgent = req.headers['user-agent'];
  if (!userAgent || !userAgent.startsWith('InitRepo-Webhooks/')) {
    return res.status(400).json({ error: 'Invalid user agent' });
  }

  // Verify timestamp (prevent replay attacks)
  const timestamp = parseInt(req.headers['x-initrepo-timestamp']);
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - timestamp) > 300) { // 5 minute tolerance
    return res.status(400).json({ error: 'Request too old' });
  }

  // Continue with signature verification...
});
```

## Error Handling

### Enhanced Retry Logic

InitRepo uses an intelligent retry system with exponential backoff and jitter to handle failed webhook deliveries:

#### Retry Schedule

1. **Attempt 1**: Immediate delivery
2. **Attempt 2**: 1 second + random jitter (0-500ms)
3. **Attempt 3**: 2 seconds + random jitter (0-1s)
4. **Attempt 4**: 4 seconds + random jitter (0-2s)
5. **Attempt 5**: 8 seconds + random jitter (0-4s)
6. **Attempt 6**: 16 seconds + random jitter (0-8s) - **Final attempt**

#### What Triggers Retries

* HTTP status codes other than 2xx (200-299)
* Network timeouts (>10 seconds)
* Connection refused or DNS resolution failures
* SSL/TLS handshake failures

#### Delivery Status Tracking

Each webhook attempt is logged with detailed status information available through the API:

```json theme={null}
{
  "webhookId": "wh_1234567890",
  "url": "https://your-app.com/webhooks/initrepo",
  "attempts": [
    {
      "attemptNumber": 1,
      "timestamp": "2024-01-15T10:45:00Z",
      "status": "failed",
      "httpStatus": 500,
      "responseTime": 2.5,
      "error": "Internal Server Error"
    },
    {
      "attemptNumber": 2,
      "timestamp": "2024-01-15T10:45:01Z", 
      "status": "success",
      "httpStatus": 200,
      "responseTime": 0.8
    }
  ],
  "finalStatus": "delivered"
}
```

### Webhook Monitoring

Access comprehensive delivery statistics through the API:

```javascript theme={null}
// Get webhook delivery stats for a project
const response = await fetch(`https://api.initrepo.com/v1/projects/${projectId}/webhooks`, {
  headers: {
    'Authorization': 'Bearer ir_live_your_api_key'
  }
});

const { webhookDeliveries } = await response.json();
```

### Timeout Configuration

* **Connection Timeout**: 5 seconds to establish connection
* **Response Timeout**: 10 seconds to receive complete response
* **Total Request Timeout**: 15 seconds maximum per attempt

### Response Requirements

* **Success**: Return HTTP status 200-299 to acknowledge receipt
* **Failure**: Any other status code triggers retry according to schedule
* **Response Body**: Ignored (can be empty or JSON)
* **Response Time**: Aim for \<5 seconds to avoid timeouts

### Error Scenarios

#### Permanent Failures (No Retries)

* URL returns 404 Not Found
* Invalid HTTPS certificate
* Webhook URL blocked by security policy

#### Temporary Failures (Will Retry)

* 5xx server errors
* Network timeouts
* Rate limiting (429 status)
* Connection refused

## Best Practices

### Idempotency

```javascript theme={null}
// Implement idempotency to handle duplicate webhooks
const processedWebhooks = new Set();

app.post('/webhooks/initrepo', (req, res) => {
  const { data } = req.body;
  const projectId = data.projectId;

  if (processedWebhooks.has(projectId)) {
    return res.status(200).json({ message: 'Already processed' });
  }

  // Process webhook
  processedWebhooks.add(projectId);

  res.status(200).json({ received: true });
});
```

### Logging and Monitoring

```javascript theme={null}
// Log all webhook attempts
app.post('/webhooks/initrepo', (req, res) => {
  const timestamp = new Date().toISOString();
  const { event, data } = req.body;

  console.log(`[${timestamp}] Webhook received: ${event} for project ${data.projectId}`);

  // Process webhook...

  res.status(200).json({ received: true });
});
```

### Rate Limiting

* Implement rate limiting on your webhook endpoint
* Expect up to 5 webhook attempts per project completion
* Handle bursts gracefully

## Testing Webhooks

### Local Development

Use tools like [ngrok](https://ngrok.com) or [localtunnel](https://localtunnel.github.io/www/) to expose local servers:

```bash theme={null}
# Using ngrok
ngrok http 3000
# Forwarding: https://abc123.ngrok.io -> http://localhost:3000

# Update webhook URL in request
{
  "webhookUrl": "https://abc123.ngrok.io/webhooks/initrepo"
}
```

### Test Mode

Use test API keys for development - webhooks work the same way but don't consume credits.

## Troubleshooting

### Webhook Not Received

1. **Check URL**: Ensure webhook URL is HTTPS and publicly accessible
2. **Verify Logs**: Check your application logs for incoming requests
3. **Test Endpoint**: Send a test POST request to your webhook URL
4. **Check Firewall**: Ensure your server accepts POST requests on the webhook path

### Duplicate Webhooks

1. **Implement Idempotency**: Use project IDs to prevent duplicate processing
2. **Check Timestamps**: Compare webhook timestamps to avoid processing old events
3. **Log Processing**: Keep track of processed webhook events

### Timeout Issues

1. **Optimize Response Time**: Ensure your webhook handler responds within 10 seconds
2. **Async Processing**: Move heavy processing to background jobs
3. **Return Early**: Send 200 response immediately, process asynchronously
