Skip to main content
POST
/
webhooks
/
bridge-events
cURL
curl -X POST https://your-crm.com/webhooks/bridge-events \
  -H "Content-Type: application/json" \
  -H "X-Bridge-API-Key: wh_1234567890abcdef" \
  -H "X-Bridge-Signature: sha256=a1b2c3d4e5f6789012345678901234567890abcdef123456789012345678901234" \
  -H "X-Bridge-Timestamp: 1642234567" \
  -d '{
    "eventId": "evt_123456789",
    "eventType": "user.daily_resume_generated",
    "entity": "user",
    "workspaceId": "ws_abc123",
    "timestamp": "2024-01-15T10:30:00Z",
    "payload": {
      "userId": "user_abc123",
      "summary": "Daily resume for John Doe"
    }
  }'
{
  "status": "received"
}

Authorizations

X-Bridge-API-Key
string
header
required

Webhook authentication for Bridge event notifications sent to your CRM.

Authentication Process:

  1. Configure a webhook API key in your Bridge dashboard
  2. Bridge will include this key in the X-Bridge-API-Key header
  3. Bridge will sign the request using HMAC-SHA256 and include the signature in X-Bridge-Signature header
  4. Bridge will include a timestamp in X-Bridge-Timestamp header

Signature Construction:

  • Create signature payload: {TIMESTAMP}{REQUEST_BODY} (timestamp + raw body, no separator)
  • Generate HMAC-SHA256 signature using your webhook secret key
  • Encode signature as hexadecimal (lowercase)
  • Send as X-Bridge-Signature: sha256={hex_signature}

Security Features:

  • Replay Attack Prevention: Timestamp verification (reject requests older than 5 minutes)
  • Message Integrity: HMAC-SHA256 ensures payload hasn't been tampered with
  • Authentication: Verifies the request originated from Bridge
  • Timing Attack Mitigation: Use constant-time comparison for signature verification

Example Headers from Bridge:

X-Bridge-API-Key: wh_1234567890abcdef
X-Bridge-Timestamp: 1642234567
X-Bridge-Signature: sha256=a1b2c3d4e5f6789012345678901234567890abcdef123456789012345678901234

Verification Steps:

  1. Extract timestamp from X-Bridge-Timestamp header
  2. Verify timestamp is within 5 minutes of current time (prevents replay attacks)
  3. Reconstruct signature payload: timestamp + request_body
  4. Generate expected signature using HMAC-SHA256 with your webhook secret
  5. Compare signatures using constant-time comparison

Verification Code Example (Node.js):

const crypto = require('crypto');

function verifyBridgeWebhook(body, signature, timestamp, secret) {
// 1. Check timestamp (reject if older than 5 minutes)
const currentTime = Math.floor(Date.now() / 1000);
const requestTime = parseInt(timestamp);
if (currentTime - requestTime > 300) { // 300 seconds = 5 minutes
throw new Error('Request timestamp too old');
}

// 2. Construct signature payload (timestamp + body, no separator)
const signaturePayload = timestamp + body;

// 3. Generate expected signature
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signaturePayload, 'utf8')
.digest('hex');

// 4. Extract received signature (remove 'sha256=' prefix)
const receivedSignature = signature.replace('sha256=', '');

// 5. Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}

// Usage example
app.post('/webhooks/bridge-events', (req, res) => {
const signature = req.headers['x-bridge-signature'];
const timestamp = req.headers['x-bridge-timestamp'];
const apiKey = req.headers['x-bridge-api-key'];
const body = req.body; // raw body as string

try {
// Verify API key
if (apiKey !== process.env.BRIDGE_WEBHOOK_API_KEY) {
return res.status(401).send('Invalid API key');
}

// Verify signature
if (!verifyBridgeWebhook(body, signature, timestamp, process.env.BRIDGE_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}

// Process webhook
console.log('Verified webhook received:', JSON.parse(body));
res.status(200).json({ status: 'received' });
} catch (error) {
console.error('Webhook verification failed:', error.message);
res.status(401).send('Verification failed');
}
});

Python Example:

import hmac
import hashlib
import time

def verify_bridge_webhook(body: str, signature: str, timestamp: str, secret: str) -> bool:
# Check timestamp (reject if older than 5 minutes)
current_time = int(time.time())
request_time = int(timestamp)
if current_time - request_time > 300:
raise ValueError("Request timestamp too old")

# Construct signature payload
signature_payload = timestamp + body

# Generate expected signature
expected_signature = hmac.new(
secret.encode('utf-8'),
signature_payload.encode('utf-8'),
hashlib.sha256
).hexdigest()

# Extract received signature
received_signature = signature.replace('sha256=', '')

# Constant-time comparison
return hmac.compare_digest(expected_signature, received_signature)

Body

application/json
eventId
string
required

Unique identifier for the event (used for idempotency)

Example:

"evt_123456789"

eventType
enum<string>
required

Type of event that occurred

Available options:
user.daily_resume_generated,
task.created,
task.updated,
task.completed,
opportunity.created,
opportunity.updated,
conversation.created,
conversation.updated,
conversation.participant_added,
conversation.participant_removed,
conversation.status_changed
Example:

"user.daily_resume_generated"

entity
enum<string>
required

The entity type that the event relates to

Available options:
user,
task,
opportunity,
conversation
Example:

"user"

workspaceId
string
required

Unique identifier of the workspace where the event occurred

Example:

"ws_abc123"

timestamp
string<date-time>
required

ISO 8601 timestamp when the event occurred

Example:

"2024-01-15T10:30:00Z"

payload
object
required

Event-specific data structure

Example:
{
"userId": "user_abc123",
"summary": "Daily resume containing 5 completed tasks",
"conversationId": "conv_abc123",
"participantType": "external",
"participantIdentifier": "john.doe@example.com"
}

Response

Event received successfully

status
string
Example:

"received"

I