Webhooks

Create custom integrations with Elixion webhooks

Webhooks

Webhooks allow you to integrate Elixion with external systems by sending HTTP requests when events occur. Build custom integrations, trigger external workflows, and sync data.

Overview

How Webhooks Work

Event Occurs → Elixion → HTTP POST → Your Endpoint
     ↓
 Issue Created    →    {"event": "issue.created", ...}
 Status Changed   →    {"event": "issue.updated", ...}
 Sprint Started   →    {"event": "sprint.started", ...}

Creating Webhooks

Basic Setup

  1. Navigate to Settings > Integrations > Webhooks
  2. Click + New Webhook
  3. Configure:
    • Name
    • URL
    • Events
    • Secret (optional)
  4. Save and test

Configuration

Webhook: Deploy Notifier
────────────────────────

URL: https://api.example.com/elixion-webhook
Method: POST
Content-Type: application/json

Authentication:
☑ Include secret header
Header: X-Webhook-Secret
Secret: ••••••••••••

Events:
☑ issue.created
☑ issue.updated
☐ issue.deleted
☑ sprint.started
☑ sprint.completed
☐ comment.created

Active: ✓ Yes

Events

Available Events

| Event | Trigger | |-------|---------| | issue.created | New issue created | | issue.updated | Issue modified | | issue.deleted | Issue deleted | | issue.moved | Issue changed project | | comment.created | New comment | | sprint.started | Sprint begins | | sprint.completed | Sprint ends | | project.created | New project | | user.joined | User joins team | | deployment.completed | Deploy finished |

Event Filtering

Filter events by conditions:

events:
  - event: issue.updated
    filters:
      - field: status
        changed_to: Done
      - field: priority
        values: [Critical, High]

  - event: issue.created
    filters:
      - field: project
        values: [AUTH, CORE]

Payload Format

Standard Payload

{
  "id": "evt_123456",
  "event": "issue.updated",
  "timestamp": "2024-01-15T10:30:00Z",
  "workspace": {
    "id": "ws_abc",
    "name": "Engineering"
  },
  "data": {
    "issue": {
      "id": "issue_xyz",
      "key": "AUTH-123",
      "title": "Login validation",
      "type": "Story",
      "status": "Done",
      "priority": "High",
      "assignee": {
        "id": "user_123",
        "name": "Alice",
        "email": "alice@example.com"
      },
      "project": {
        "id": "proj_abc",
        "key": "AUTH",
        "name": "Authentication"
      }
    },
    "changes": [
      {
        "field": "status",
        "old_value": "In Progress",
        "new_value": "Done"
      }
    ],
    "actor": {
      "id": "user_456",
      "name": "Bob"
    }
  }
}

Event-Specific Payloads

issue.created:

{
  "event": "issue.created",
  "data": {
    "issue": { /* full issue object */ },
    "actor": { /* who created it */ }
  }
}

sprint.started:

{
  "event": "sprint.started",
  "data": {
    "sprint": {
      "id": "sprint_123",
      "name": "Sprint 15",
      "goal": "Complete authentication",
      "start_date": "2024-01-08",
      "end_date": "2024-01-19",
      "issues_count": 15,
      "points": 45
    },
    "project": { /* project object */ }
  }
}

Security

Webhook Secrets

Verify webhook authenticity:

import hmac
import hashlib

def verify_webhook(payload, signature, secret):
    expected = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(
        f"sha256={expected}",
        signature
    )

# In your handler
signature = request.headers.get('X-Elixion-Signature')
if not verify_webhook(request.body, signature, WEBHOOK_SECRET):
    return Response(status=401)

Headers

Included headers:

| Header | Description | |--------|-------------| | X-Elixion-Signature | HMAC signature | | X-Elixion-Event | Event type | | X-Elixion-Delivery | Unique delivery ID | | X-Elixion-Timestamp | Event timestamp |

IP Allowlisting

Elixion webhook IPs:

52.xxx.xxx.xxx/32
52.xxx.xxx.xxx/32

Handling Webhooks

Basic Handler

// Express.js example
app.post('/webhook', express.json(), (req, res) => {
  const event = req.body;

  switch (event.event) {
    case 'issue.created':
      handleNewIssue(event.data.issue);
      break;
    case 'issue.updated':
      handleIssueUpdate(event.data);
      break;
    case 'sprint.completed':
      handleSprintEnd(event.data.sprint);
      break;
  }

  res.status(200).json({ received: true });
});

Async Processing

For long-running tasks:

app.post('/webhook', (req, res) => {
  // Acknowledge immediately
  res.status(200).json({ received: true });

  // Process async
  queue.add('process-webhook', req.body);
});

Idempotency

Handle duplicate deliveries:

const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const deliveryId = req.headers['x-elixion-delivery'];

  if (processedEvents.has(deliveryId)) {
    return res.status(200).json({ duplicate: true });
  }

  processedEvents.add(deliveryId);
  // Process event...
});

Testing

Test Delivery

Send test webhook:

  1. Open webhook settings
  2. Click Test Webhook
  3. Select sample event
  4. Review delivery

Delivery Logs

View webhook history:

Recent Deliveries
─────────────────

Delivery ID        Event              Status    Time
──────────────────────────────────────────────────────
del_abc123        issue.updated      ✓ 200     2.3s
del_abc122        sprint.completed   ✓ 200     1.8s
del_abc121        issue.created      ✗ 500     -
del_abc120        comment.created    ✓ 200     0.9s

[View Details] [Retry Failed]

Debugging

View delivery details:

Delivery: del_abc121
Status: ✗ Failed (500)

Request:
POST https://api.example.com/webhook
Headers: { ... }
Body: { "event": "issue.created", ... }

Response:
Status: 500 Internal Server Error
Body: { "error": "Database connection failed" }

Retry: [Retry Now] [Schedule]

Retry Policy

Automatic Retries

Failed webhooks are retried:

Retry Schedule:
1st retry:  1 minute
2nd retry:  5 minutes
3rd retry:  30 minutes
4th retry:  2 hours
5th retry:  12 hours

Success Criteria

Webhook considered successful if:

  • HTTP status 2xx
  • Response within 30 seconds

Disable on Failure

After repeated failures:

  • 10 consecutive failures
  • Webhook auto-disabled
  • Admin notified

Use Cases

CI/CD Trigger

# Trigger deployment on merge
event: issue.updated
condition: status == "Done" AND labels.includes("deploy")
action: POST to CI/CD system

Slack Custom Bot

// Custom Slack formatting
app.post('/webhook', (req, res) => {
  const { event, data } = req.body;

  if (event === 'issue.created' && data.issue.priority === 'Critical') {
    slack.postMessage({
      channel: '#urgent',
      text: `🚨 Critical issue: ${data.issue.key}`,
      attachments: [/* custom formatting */]
    });
  }
});

External Analytics

// Track metrics externally
app.post('/webhook', (req, res) => {
  const { event, data } = req.body;

  analytics.track({
    event: `elixion.${event}`,
    properties: {
      project: data.issue?.project?.key,
      type: data.issue?.type,
      user: data.actor?.id
    }
  });
});

Next: Dashboards - Visual insights