Skip to main content
Back to ScopeForged

ScopeForged Documentation

Technical documentation, guides, and feature references for the ScopeForged client portal.

Communication & Real-time/Webhooks

Webhooks Guide

Last Updated: 2026-01-10 Status: Implemented Plan Reference: 023-webhooks-event-integration.md, 059-webhooks-enhancement.md


Overview

The Webhooks system enables external integrations by sending HTTP callbacks when events occur in the portal. Third-party systems can subscribe to events like invoice creation, project updates, and file uploads to automate workflows and keep systems in sync.


Table of Contents

  1. Accessing Webhooks
  2. Features
  3. How to Use
  4. Event Types
  5. Payload Format
  6. Signature Verification
  7. Interactive Testing
  8. Delivery Monitoring
  9. Technical Architecture
  10. Related Features

Accessing Webhooks

Access PointLocationURLRole
Webhook ManagementAdmin sidebar/admin/webhooksAdmin
Create WebhookWebhooks page/admin/webhooks/createAdmin
Webhook DetailsWebhook list/admin/webhooks/{id}Admin
Test InterfaceWebhook detail/admin/webhooks/{id}/test-formAdmin
Delivery LogsWebhook detail/admin/webhooks/{id}/deliveriesAdmin
All DeliveriesWebhooks menu/admin/webhooks-all-deliveriesAdmin
DocumentationWebhooks menu/admin/webhooks-documentationAdmin

Permissions

ActionAdminClient User
View webhooks
Create webhooks
Edit webhooks
Delete webhooks
View logs
Test webhooks
View documentation

Features

Webhook Management

  • Create multiple webhooks per client or global
  • Subscribe to specific events
  • Custom headers support
  • Enable/disable toggle
  • Auto-generated or custom secret key
  • Regenerate secret on demand

Configuration Options

  • Timeout: Configurable timeout per endpoint (default 30s)
  • Max Retries: Configurable retry attempts (default 5)
  • Custom Headers: Add custom headers to requests
  • Payload Logging: Toggle payload logging for debugging

Interactive Testing (New)

  • Visual testing interface
  • Select from all event types
  • Edit payloads with JSON validation
  • View real-time results
  • Inspect request/response headers
  • Verify signature generation

Delivery Monitoring (Enhanced)

  • Delivery attempt history
  • Request/response headers logging
  • Error message tracking
  • Duration metrics
  • Global delivery dashboard
  • Filter by endpoint, status, event, date

Security

  • HMAC-SHA256 signature verification
  • Timestamp validation (replay attack prevention)
  • Secret key per webhook
  • HTTPS enforcement (production)
  • Request timeout limits

How to Use

Creating a Webhook

  1. Navigate to Admin → Webhooks
  2. Click "Create Webhook"
  3. Fill in details:
    • Name: Descriptive name
    • URL: Endpoint to receive callbacks
    • Client: Optional - associate with specific client
    • Events: Select events to subscribe
  4. Configure advanced options:
    • Timeout: Request timeout (1-60 seconds)
    • Max Retries: Retry attempts (1-10)
    • Custom Headers: Add key-value pairs
  5. Click "Create Webhook"
  6. Save the secret key - shown only once

Testing a Webhook (Interactive)

  1. Navigate to webhook detail page
  2. Click "Test" button
  3. In the testing interface:
    • Select event type from dropdown
    • View/edit the sample payload
    • Click "Send Test Webhook"
  4. Review results:
    • Success/failure status
    • HTTP status code
    • Response duration
    • Signature used
    • Request/response headers
    • Response body

Viewing Delivery Logs

  1. Navigate to webhook detail page
  2. Click "Deliveries" tab
  3. View delivery attempts:
    • Delivery ID
    • Event type
    • Status (pending/success/failed)
    • Response code
    • Duration
    • Attempt count
  4. Click a delivery for detailed view:
    • Full request/response headers
    • Payload JSON
    • Individual attempt history
    • Error messages

Global Delivery Monitoring

  1. Navigate to Admin → Webhooks → All Deliveries
  2. View aggregated statistics:
    • Total deliveries
    • Success/failure counts
    • Success rate percentage
    • Average duration
    • Failed in last 24h
  3. Filter by:
    • Endpoint
    • Status
    • Event type
    • Date range

Retrying Failed Deliveries

  1. Navigate to delivery detail or all deliveries
  2. Click "Retry" button on failed delivery
  3. Delivery is re-queued with reset attempt count

Event Types

Client Events

EventTriggerPayload
client.createdNew client createdClient data
client.updatedClient info updatedClient data
client.deletedClient deletedClient ID

Project Events

EventTriggerPayload
project.createdNew project createdProject data
project.updatedProject updatedProject data
project.status_changedStatus changedProject + old/new status
project.deletedProject deletedProject ID

Invoice Events

EventTriggerPayload
invoice.createdInvoice createdInvoice data
invoice.updatedInvoice updatedInvoice data
invoice.sentInvoice marked sentInvoice + sent_to
invoice.paidInvoice marked paidInvoice + payment info
invoice.overdueInvoice became overdueInvoice + days overdue
invoice.deletedInvoice deletedInvoice ID

File Events

EventTriggerPayload
file.uploadedFile uploadedFile metadata
file.deletedFile deletedFile ID

User Events

EventTriggerPayload
user.createdNew user createdUser data
user.updatedUser updatedUser data

System Events

EventTriggerPayload
webhook.testTest webhook sentTest message

Payload Format

Standard Payload Structure

{
  "id": "test_abc123def456",
  "event": "invoice.created",
  "created_at": "2026-01-10T14:30:00+00:00",
  "test": false,
  "timestamp": "2026-01-10T14:30:00+00:00",
  "data": {
    "id": 12345,
    "number": "INV-2026-0001",
    "client_id": 1,
    "total": 1650.00,
    "currency": "USD",
    "status": "draft"
  }
}

Request Headers

Content-Type: application/json
Accept: application/json
X-Webhook-Signature: t=1704898200,v1=abc123...
X-Webhook-Event: invoice.created
X-Webhook-Endpoint-Id: 1
X-Webhook-Test: false
User-Agent: ClientPortal-Webhook/1.0

Signature Verification

Signature Format

Each request includes an X-Webhook-Signature header:

t=1704898200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Where:

  • t: Unix timestamp when signature was created
  • v1: HMAC-SHA256 signature

Signed Payload

The signature is computed over:

{timestamp}.{json_payload}

Verification Steps

  1. Parse the signature header to extract t and v1
  2. Check timestamp is within 5 minutes (prevents replay attacks)
  3. Compute expected signature: HMAC-SHA256(secret, "{t}.{payload}")
  4. Compare using constant-time comparison

PHP Example

<?php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'];
$secret = 'your_webhook_secret';

// Parse signature
preg_match('/t=(\d+),v1=(.+)/', $signature, $matches);
$timestamp = $matches[1];
$providedSignature = $matches[2];

// Verify timestamp (within 5 minutes)
if (abs(time() - $timestamp) > 300) {
    http_response_code(401);
    exit('Signature expired');
}

// Verify signature
$expectedSignature = hash_hmac('sha256', "{$timestamp}.{$payload}", $secret);

if (!hash_equals($expectedSignature, $providedSignature)) {
    http_response_code(401);
    exit('Invalid signature');
}

// Process webhook...
$data = json_decode($payload, true);

Node.js Example

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
    const match = signature.match(/t=(\d+),v1=(.+)/);
    if (!match) return false;

    const timestamp = parseInt(match[1]);
    const providedSignature = match[2];

    // Check timestamp (within 5 minutes)
    if (Math.abs(Date.now() / 1000 - timestamp) > 300) {
        return false;
    }

    // Verify signature
    const expectedSignature = crypto
        .createHmac('sha256', secret)
        .update(`${timestamp}.${payload}`)
        .digest('hex');

    return crypto.timingSafeEqual(
        Buffer.from(expectedSignature),
        Buffer.from(providedSignature)
    );
}

// Express middleware example
app.post('/webhook', (req, res) => {
    const payload = JSON.stringify(req.body);
    const signature = req.headers['x-webhook-signature'];

    if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
        return res.status(401).send('Invalid signature');
    }

    // Process webhook...
    res.status(200).send('OK');
});

Python Example

import hmac
import hashlib
import time
import re

def verify_webhook(payload: str, signature: str, secret: str) -> bool:
    match = re.match(r't=(\d+),v1=(.+)', signature)
    if not match:
        return False

    timestamp = int(match.group(1))
    provided_signature = match.group(2)

    # Check timestamp (within 5 minutes)
    if abs(time.time() - timestamp) > 300:
        return False

    # Verify signature
    expected_signature = hmac.new(
        secret.encode(),
        f"{timestamp}.{payload}".encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected_signature, provided_signature)

# Flask example
@app.route('/webhook', methods=['POST'])
def handle_webhook():
    payload = request.get_data(as_text=True)
    signature = request.headers.get('X-Webhook-Signature')

    if not verify_webhook(payload, signature, os.environ['WEBHOOK_SECRET']):
        return 'Invalid signature', 401

    # Process webhook...
    data = request.get_json()
    return 'OK', 200

Interactive Testing

The interactive testing interface allows you to send test webhooks with custom payloads.

Features

  • Event Selection: Choose from all available event types
  • Sample Payloads: Pre-populated with realistic sample data
  • JSON Editing: Edit payload with real-time validation
  • Result Display: View success/failure, status code, duration
  • Header Inspection: View exact request/response headers
  • Signature Display: Copy signature for testing verification

Accessing the Test Interface

  1. Navigate to Admin → Webhooks
  2. Click on a webhook to view details
  3. Click "Test" button
  4. Use the interactive testing panel

Test Headers

Test requests include additional headers:

  • X-Webhook-Test: true - Indicates this is a test
  • Standard webhook headers apply

Delivery Monitoring

Delivery States

StatusDescription
pendingAwaiting delivery or retry
successDelivered successfully (2xx response)
failedAll retries exhausted

Attempt History

Each delivery tracks individual attempts:

  • Attempt number
  • Timestamp
  • Response status
  • Response headers
  • Response body
  • Duration (ms)
  • Error message (if any)

Retry Policy

AttemptDelayTotal Time
1Immediate0
21 minute1 minute
35 minutes6 minutes
430 minutes36 minutes
52 hours2h 36m

After all retries fail, the delivery is marked as failed.


Technical Architecture

Models

WebhookEndpoint: app/Models/WebhookEndpoint.php

class WebhookEndpoint extends Model
{
    protected $fillable = [
        'name', 'url', 'client_id', 'events', 'secret',
        'is_active', 'custom_headers', 'timeout_seconds',
        'max_retries', 'log_payloads', 'created_by',
    ];

    protected $casts = [
        'events' => 'array',
        'custom_headers' => 'array',
        'is_active' => 'boolean',
        'log_payloads' => 'boolean',
    ];
}

WebhookDelivery: app/Models/WebhookDelivery.php

class WebhookDelivery extends Model
{
    protected $fillable = [
        'webhook_endpoint_id', 'event_type', 'payload',
        'request_headers', 'status', 'response_code',
        'response_headers', 'response_body', 'duration_ms',
        'attempts', 'error_message', 'next_retry_at',
    ];

    public function attempts(): HasMany
    {
        return $this->hasMany(WebhookDeliveryAttempt::class);
    }
}

WebhookDeliveryAttempt: app/Models/WebhookDeliveryAttempt.php

class WebhookDeliveryAttempt extends Model
{
    protected $fillable = [
        'webhook_delivery_id', 'attempt_number',
        'request_headers', 'response_status',
        'response_headers', 'response_body',
        'duration_ms', 'error_message', 'attempted_at',
    ];
}

Services

WebhookService: app/Services/WebhookService.php

  • Endpoint CRUD operations
  • Webhook dispatching
  • Stats calculation

WebhookTesterService: app/Services/Webhooks/WebhookTesterService.php

  • Send test webhooks
  • Generate sample payloads
  • Provide signature documentation

Database Tables

Table: webhook_endpoints

ColumnTypeDescription
idbigintPrimary key
namestringWebhook name
urlstringEndpoint URL
client_idbigintOptional client FK
eventsjsonSubscribed events
secretstringSigning secret
custom_headersjsonCustom headers
timeout_secondsintRequest timeout
max_retriesintMax retry attempts
log_payloadsbooleanLog payloads
is_activebooleanEnabled status
created_bybigintCreator user FK

Table: webhook_deliveries

ColumnTypeDescription
iduuidPrimary key
webhook_endpoint_idbigintEndpoint FK
event_typestringEvent type
payloadjsonSent payload
request_headersjsonRequest headers
statusstringpending/success/failed
response_codeintHTTP status
response_headersjsonResponse headers
response_bodytextResponse body
duration_msintResponse time
attemptsintAttempt count
error_messagetextError details
next_retry_attimestampNext retry time

Table: webhook_delivery_attempts

ColumnTypeDescription
idbigintPrimary key
webhook_delivery_iduuidDelivery FK
attempt_numberintAttempt sequence
request_headersjsonRequest headers
response_statusintHTTP status
response_headersjsonResponse headers
response_bodytextResponse body
duration_msintDuration
error_messagetextError details
attempted_attimestampAttempt time

Dependencies

FeatureRelationship
Background JobsQueued delivery
AuthenticationAdmin access required

Complementary Features

FeatureDescription
NotificationsInternal notifications
API GuideAPI integration
Activity LoggingWebhook event logs

Best Practices

For Administrators

  1. Use HTTPS URLs for security
  2. Store secrets securely in receiving system
  3. Monitor delivery logs for failures
  4. Use interactive testing before going live
  5. Review documentation page for signature examples

For Developers

  1. Always verify signatures before processing
  2. Check timestamp to prevent replay attacks
  3. Respond quickly (< 30 seconds)
  4. Return 2xx for success to prevent retries
  5. Queue heavy processing after acknowledgment
  6. Implement idempotency using event ID

Troubleshooting

IssueSolution
Webhook not triggeringCheck is_active and event subscription
Signature invalidVerify secret and timestamp format
TimeoutsIncrease timeout or process async
Retries failingCheck endpoint availability
Test fails but real events workCheck X-Webhook-Test header handling

See Also