Skip to main content
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:
{
  "projectIdea": "Build a task management app",
  "projectType": "web",
  "webhookUrl": "https://your-app.com/webhooks/initrepo"
}

Webhook Payload

{
  "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"
    }
  }
}

Webhook Headers

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

event
string
Always “project.completed” for completion notifications.
timestamp
string
ISO 8601 timestamp when the webhook was sent.
data.projectId
string
Unique identifier of the completed project.
data.project.name
string
Generated project name.
data.project.status
string
Always “completed” for success notifications.
data.project.creditsConsumed
integer
Total credits consumed for this project.
data.project.createdAt
string
ISO 8601 timestamp when the project was created.
data.project.completedAt
string
ISO 8601 timestamp when the project completed.

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.
Security NoticeWebhook signature verification is required for security. Unsigned webhooks should be rejected to prevent spoofing attacks.

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

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

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

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:
{
  "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:
// 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

// 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

// 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 or localtunnel to expose local servers:
# 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