Webhooks

Diversion supports webhooks to notify external services when events occur in your repositories. This enables integration with CI/CD systems, notification services, and custom automation workflows.

Overview

Webhooks are HTTP callbacks that are triggered by specific repository events. When an event occurs, Diversion sends a POST request to the configured endpoint URL with event details in the request body.

Key Features

  • Event-driven notifications: Real-time notifications for repository events
  • Secure delivery: HMAC-SHA256 signature verification for request authenticity
  • Reliable delivery: Automatic retries with exponential backoff
  • Flexible configuration: Subscribe to specific event types per webhook
  • Admin-only management: Repository admin access required

Supported Events

Currently, Diversion supports the following webhook events:
EventDescriptionPayload
commit_createdTriggered when a new commit is createdCommit details, repository info, author

Managing Webhooks

Webhooks can be managed through the web application. Access requires repository admin permissions.

Web Interface

Navigate to Settings → Webhooks for repository-specific webhooks Webhook Management Interface

Creating a Webhook

  1. Click “Create Webhook”
  2. Fill in the webhook details:
    • Name: Descriptive name for the webhook
    • Description: Optional description of the webhook’s purpose
    • Endpoint URL: HTTPS URL where events will be sent
    • Secret: Webhook secret for signature verification (auto-generated if not provided)
    • Events: Select which events should trigger the webhook
  3. Click “Create” to save the webhook
Create Webhook Form

Webhook Secret

The webhook secret is used to generate HMAC-SHA256 signatures for request verification. Secrets must be:
  • Between 24-75 characters long
  • Stored encrypted in the database
  • Only visible during webhook creation
Auto-generation: If no secret is provided, Diversion generates a cryptographically secure 32-byte secret in the format: whsec_<base64-encoded-random-bytes>

Webhook Payload

All webhook payloads follow a consistent structure:
{
  "id": "evt_1234567890",
  "timestamp": "2025-01-17T12:34:56.789Z",
  "type": "v1.repo.commit.created",
  "data": {
    // Event-specific data
  }
}

Commit Created Event Payload

{
  "id": "evt_2KtGQeeJhtF5qQ",
  "timestamp": "2025-01-17T08:23:15.123Z",
  "type": "v1.repo.commit.created",
  "data": {
    "repository": {
      "id": "dv.repo.12345678-1234-1234-1234-123456789abc",
      "name": "my-project",
      "owner_id": "Google_12345678901234567890"
    },
    "commit": {
      "id": "dv.commit.123456",
      "url": "https://app.diversion.dev/r/my-project/commit/dv.commit.123456",
      "message": "Add new feature",
      "timestamp": "2025-01-17T08:23:14.567Z",
      "branch": {
        "id": 42,
        "name": "feature-branch"
      },
      "author": {
        "id": "Google_12345678901234567890",
        "name": "John Doe",
        "email": "john.doe@example.com"
      },
      "parents": ["dv.commit.123455"]
    },
    "correlation_id": "abc123-def456-ghi789"
  }
}

Request Signature Verification

All webhook requests include signature headers for verification:
  • webhook-id: Unique message identifier
  • webhook-timestamp: Request timestamp (Unix epoch in seconds)
  • webhook-signature: HMAC-SHA256 signature (format: v1,base64signature)

Signature Verification Example

Diversion provides a reference implementation for webhook signature verification. Here’s an example in Python:
import base64
import hashlib
import hmac

def verify_webhook_signature(webhook_id, webhook_timestamp, sig_header, secret, payload_body):
    # Svix signature format: v1,base64signature
    if not sig_header.startswith('v1,'):
        print(f"Invalid signature format: {sig_header}")
        return False
    
    # Extract the signature part after v1,
    v1_signature = sig_header[3:]  # Skip "v1,"
    
    # Use raw hex format directly as the signing key
    try:
        # Svix uses the literal hex string as UTF-8 bytes for signing
        signing_key = secret.encode('utf-8')
    except Exception as e:
        print(f"Failed to encode secret: {e}")
        return False
    
    # Construct the signed content according to Svix docs
    # Format: {id}.{timestamp}.{payload}
    signed_content = f"{webhook_id}.{webhook_timestamp}.{payload_body.decode('utf-8')}"
    
    # Compute HMAC-SHA256
    computed_hmac = hmac.new(
        signing_key,  # Use the hex string encoded as UTF-8 bytes
        signed_content.encode('utf-8'),
        hashlib.sha256  
    ).digest()
    
    # Base64 encode the result
    expected_sig = base64.b64encode(computed_hmac).decode('utf-8')
    
    # Compare signatures
    is_valid = hmac.compare_digest(expected_sig, v1_signature)
    
    if is_valid:
        print("Svix signature verified successfully")
    else:
        print(f"Signature verification failed. Expected: {expected_sig}, Got: {v1_signature}")
    
    return is_valid
Note: A fully tested webhook validation implementation is available as an AWS Lambda function example. Contact support if you need assistance with webhook signature verification.

Webhook Delivery

Diversion ensures reliable webhook delivery with the following features:
  • Automatic retries: Failed deliveries are retried with exponential backoff
  • Delivery tracking: Monitor webhook delivery status
  • Rate limiting: Prevents overwhelming destination servers
  • Circuit breaking: Temporarily disables failing endpoints

Delivery Guarantees

  • Webhooks are delivered at least once
  • Events may arrive out of order
  • Use the timestamp field for event ordering
  • Implement idempotent handlers using the id field

Testing Webhooks

To test your signature verification implementation:
  1. Create a test webhook with a known secret
  2. Use the example validation code provided above
  3. Compare against the reference AWS Lambda implementation

Best Practices

  1. Use HTTPS endpoints: HTTP endpoints are not supported for security
  2. Verify signatures: Always verify webhook signatures in production
  3. Handle retries: Make webhook handlers idempotent
  4. Respond quickly: Return 2xx status within 30 seconds
  5. Queue processing: For long-running tasks, queue work and respond immediately
  6. Monitor failures: Set up alerts for webhook delivery failures

Troubleshooting

Common Issues

  1. Webhook not triggering
    • Verify the webhook is active
    • Check event type subscriptions
    • Ensure you have admin access to the repository
  2. Signature verification failing
    • Confirm you’re using the correct secret
    • Verify timestamp is within tolerance (5 minutes)
    • Check signature format and parsing
  3. Delivery failures
    • Ensure endpoint is publicly accessible
    • Check for SSL/TLS certificate issues
    • Verify endpoint returns 2xx status code
    • Review server logs for errors

Debugging Tips

  • Use webhook testing tools like webhook.site for initial testing
  • Enable debug logging in your webhook handler
  • Test with curl using the expected payload format
  • Contact support if you need help investigating delivery issues

Security Considerations

  1. Secret management: Store webhook secrets securely, never in code
  2. Payload validation: Validate all webhook data before processing
  3. Rate limiting: Implement rate limiting on webhook endpoints
  4. Authentication: Use webhook signatures as the only authentication method
  5. HTTPS only: Always use HTTPS endpoints for webhook delivery

Discord Webhook Integration

At the moment, direct Discord webhook integration is not supported. However, you can still connect Discord using a middleware service such as Zapier or Pipedream, which act as bridges between Discord and our platform.

Using Pipedream to Integrate Discord Webhooks

  1. Set Up Pipedream
    • Create a Pipedream account and a new project
    • Name your project and click Create Project
  2. Create a Workflow
    • Click + New → choose Workflow
    • Name your workflow, uncheck “Send error notifications”, and click Create Workflow
  3. Add a Trigger
    • Click Add Trigger → select New HTTP / Webhook Requests
    • Click Save and Continue
  4. Add Discord Action
    • Click the + symbol below your trigger → search and select Discord.
    • Choose Send Message
    • Select your Discord account
    • Log in to Discord and select your server and channel
    • Enter your message (e.g., “New commit!”) and close the action
    • Here is a template for a more detailed message that includes the repo name, author, branch, commit id, commit message and a link to the commit:
        **New Commit to {{ JSON.parse(JSON.stringify(steps.trigger.event.body)).data?.repository_name || 'Unknown Repo' }}**
    
        **Author:** {{ JSON.parse(JSON.stringify(steps.trigger.event.body)).data?.author?.full_name || 'Unknown Author' }}
        **Branch:** {{ JSON.parse(JSON.stringify(steps.trigger.event.body)).data?.branch_name || 'Unknown Branch' }}
        **Commit ID:** {{ JSON.parse(JSON.stringify(steps.trigger.event.body)).data?.commit_id || 'Unknown ID' }}
        **Message:** {{ JSON.parse(JSON.stringify(steps.trigger.event.body)).data?.message || 'No message' }}
        **Link:** {{ JSON.parse(JSON.stringify(steps.trigger.event.body)).data?.commit_url || 'No URL' }}
    
    • You can customize the message further by accessing other available fields from the commit created event payload
  5. Test & Deploy
    • Click Test to confirm the message appears in Discord
    • Click on the trigger → copy the webhook URL
    • Use this URL as the Payload URL when configuring your webhook in the Diversion app
    • Click Deploy to activate the workflow
    • Go to Diversion and paste this URL into Diversion and add the webhook
  6. Configure the Webhook in Diversion
    • Open the Diversion Desktop App or Web UI
    • Navigate to SettingsWebhooks
    • Click Add Webhook
    • In the Payload URL field, paste the webhook URL you copied from Pipedream
    • Fill in the other webhook details
    • Click Add webhook to activate the webhook