Skip to main content

Communications Webhooks

Webhook Configuration

To set up webhooks for your integration, visit the Unizo Console Webhooks section for step-by-step configuration guide.

Overview

Unizo's Communications API provides webhooks to notify your application when important communication events occur across your integrated platforms. These real-time notifications enable you to build responsive applications that react immediately to message activity, channel updates, and user interactions.

Our platform normalizes webhook events from various communication providers (Slack, Microsoft Teams, Discord, etc.) into a consistent format, making it easy to handle events regardless of the underlying platform.

Supported Event Types

Event TypeDescriptionTrigger Conditions
Triggered when a new message is sent
Triggered when a message is edited or updated
Triggered when a message is deleted
Triggered when a new channel or conversation is created
Triggered when channel properties are modified
Triggered when a channel is archived
Triggered when a user joins a channel
Triggered when a user leaves a channel
Triggered when a reaction is added to a message
Triggered when a reaction is removed from a message

Webhook Security

All webhooks from Unizo include security headers to verify authenticity:

Headers

HeaderDescription
x-unizo-event-typeThe type of event that triggered the webhook
x-unizo-signatureHMAC SHA-256 signature for request validation
x-unizo-timestampUnix timestamp when the event was sent
x-unizo-delivery-idUnique identifier for this webhook delivery

Signature Verification

Verify the authenticity of incoming webhooks using HMAC SHA-256:

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

Event Details

Message Events

Message Sent

message:sent

Triggered when a new message is posted in any channel or direct message conversation across your connected communication platforms.
POSThttps://api.yourapp.com/webhooks/unizo/scm
Best Practice: Use a dedicated webhook endpoint that can handle multiple event types. You have two architectural options:
• Single endpoint: https://api.yourapp.com/webhooks/unizo - Route all events to one handler
• Category-based endpoints: https://api.yourapp.com/webhooks/unizo/scm - Route by category (scm, ticketing, etc.) for microservices architecture
Headers
NameTypeRequiredDescription
Content-TypestringYesAlways application/json
x-unizo-event-typestringYesEvent type: message:sent
x-unizo-webhook-idstringYesUnique webhook configuration ID
x-unizo-delivery-idstringYesUnique delivery attempt ID
x-unizo-signaturestringYesHMAC SHA256 signature for verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
data.message.idstringYesUnique message identifier
data.message.contentstringYesMessage text content
data.message.channel_idstringYesChannel where message was sent
data.message.author.idstringYesAuthor's user ID
data.message.author.namestringYesAuthor's display name
data.message.author.emailstringNoAuthor's email address
data.message.timestampstringYesMessage creation time (ISO 8601)
data.message.attachmentsarrayNoMessage attachments
data.message.mentionsarrayNoUsers or channels mentioned
integration.idstringYesIntegration ID
integration.namestringYesIntegration name
integration.providerstringYesProvider name (e.g., slack, teams)
Example Payload
{
"type": "message:sent",
"version": "1.0.0",
"data": {
"message": {
"id": "msg_abc123",
"content": "Hello team! Just pushed the latest updates to production.",
"channel_id": "ch_general",
"author": {
"id": "usr_123456",
"name": "Jane Smith",
"email": "jane.smith@company.com"
},
"timestamp": "2024-06-15T14:30:00Z",
"attachments": [],
"mentions": []
}
},
"integration": {
"id": "int_slack_789",
"name": "Company Slack",
"provider": "slack"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Message Updated

message:updated

Triggered when an existing message is edited or updated in any connected communication platform.
POSThttps://api.yourapp.com/webhooks/unizo/scm
Best Practice: Use a dedicated webhook endpoint that can handle multiple event types. You have two architectural options:
• Single endpoint: https://api.yourapp.com/webhooks/unizo - Route all events to one handler
• Category-based endpoints: https://api.yourapp.com/webhooks/unizo/scm - Route by category (scm, ticketing, etc.) for microservices architecture
Headers
NameTypeRequiredDescription
Content-TypestringYesAlways application/json
x-unizo-event-typestringYesEvent type: message:updated
x-unizo-webhook-idstringYesUnique webhook configuration ID
x-unizo-delivery-idstringYesUnique delivery attempt ID
x-unizo-signaturestringYesHMAC SHA256 signature for verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
data.message.idstringYesUnique message identifier
data.message.contentstringYesUpdated message content
data.message.previous_contentstringNoPrevious message content
data.message.channel_idstringYesChannel ID
data.message.edited_atstringYesEdit timestamp (ISO 8601)
data.message.edited_by.idstringYesEditor's user ID
data.message.edited_by.namestringYesEditor's display name
integration.idstringYesIntegration ID
integration.namestringYesIntegration name
integration.providerstringYesProvider name
Example Payload
{
"type": "message:updated",
"version": "1.0.0",
"data": {
"message": {
"id": "msg_abc123",
"content": "Hello team! Just pushed the latest updates to production. ✅",
"previous_content": "Hello team! Just pushed the latest updates to production.",
"channel_id": "ch_general",
"edited_at": "2024-06-15T14:35:00Z",
"edited_by": {
"id": "usr_123456",
"name": "Jane Smith"
}
}
},
"integration": {
"id": "int_slack_789",
"name": "Company Slack",
"provider": "slack"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Channel Events

Channel Created

channel:created

Triggered when a new channel or conversation is created in your connected communication platforms.
POSThttps://api.yourapp.com/webhooks/unizo/scm
Best Practice: Use a dedicated webhook endpoint that can handle multiple event types. You have two architectural options:
• Single endpoint: https://api.yourapp.com/webhooks/unizo - Route all events to one handler
• Category-based endpoints: https://api.yourapp.com/webhooks/unizo/scm - Route by category (scm, ticketing, etc.) for microservices architecture
Headers
NameTypeRequiredDescription
Content-TypestringYesAlways application/json
x-unizo-event-typestringYesEvent type: channel:created
x-unizo-webhook-idstringYesUnique webhook configuration ID
x-unizo-delivery-idstringYesUnique delivery attempt ID
x-unizo-signaturestringYesHMAC SHA256 signature for verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
data.channel.idstringYesUnique channel identifier
data.channel.namestringYesChannel name
data.channel.descriptionstringNoChannel description
data.channel.typestringYesChannel type (public, private, direct)
data.channel.created_by.idstringYesCreator's user ID
data.channel.created_by.namestringYesCreator's display name
data.channel.created_atstringYesCreation timestamp (ISO 8601)
data.channel.member_countintegerNoInitial member count
integration.idstringYesIntegration ID
integration.namestringYesIntegration name
integration.providerstringYesProvider name
Example Payload
{
"type": "channel:created",
"version": "1.0.0",
"data": {
"channel": {
"id": "ch_project_alpha",
"name": "project-alpha",
"description": "Discussion channel for Project Alpha development",
"type": "public",
"created_by": {
"id": "usr_789012",
"name": "John Doe"
},
"created_at": "2024-06-15T10:00:00Z",
"member_count": 5
}
},
"integration": {
"id": "int_teams_456",
"name": "Company Teams",
"provider": "microsoft_teams"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Member Events

Member Joined

member:joined

Triggered when a user joins a channel or is added to a conversation.
POSThttps://api.yourapp.com/webhooks/unizo/scm
Best Practice: Use a dedicated webhook endpoint that can handle multiple event types. You have two architectural options:
• Single endpoint: https://api.yourapp.com/webhooks/unizo - Route all events to one handler
• Category-based endpoints: https://api.yourapp.com/webhooks/unizo/scm - Route by category (scm, ticketing, etc.) for microservices architecture
Headers
NameTypeRequiredDescription
Content-TypestringYesAlways application/json
x-unizo-event-typestringYesEvent type: member:joined
x-unizo-webhook-idstringYesUnique webhook configuration ID
x-unizo-delivery-idstringYesUnique delivery attempt ID
x-unizo-signaturestringYesHMAC SHA256 signature for verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
data.channel_idstringYesChannel ID
data.channel_namestringYesChannel name
data.member.idstringYesUser ID
data.member.namestringYesUser display name
data.member.emailstringNoUser email
data.invited_by.idstringNoInviter's user ID
data.invited_by.namestringNoInviter's display name
data.joined_atstringYesJoin timestamp (ISO 8601)
integration.idstringYesIntegration ID
integration.namestringYesIntegration name
integration.providerstringYesProvider name
Example Payload
{
"type": "member:joined",
"version": "1.0.0",
"data": {
"channel_id": "ch_project_alpha",
"channel_name": "project-alpha",
"member": {
"id": "usr_345678",
"name": "Sarah Johnson",
"email": "sarah.johnson@company.com"
},
"invited_by": {
"id": "usr_789012",
"name": "John Doe"
},
"joined_at": "2024-06-15T11:30:00Z"
},
"integration": {
"id": "int_slack_789",
"name": "Company Slack",
"provider": "slack"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Webhook Delivery & Retries

Unizo implements a robust delivery system with automatic retries to ensure your webhooks are delivered reliably:

  • Timeout: 30 seconds per delivery attempt
  • Retry Schedule: 5 attempts with exponential backoff
    • Attempt 1: Immediate
    • Attempt 2: 1 minute delay
    • Attempt 3: 5 minutes delay
    • Attempt 4: 30 minutes delay
    • Attempt 5: 2 hours delay
  • Success Criteria: HTTP status codes 200-299
  • Failure Handling: After 5 failed attempts, the webhook is marked as failed

Best Practices

1. Idempotency

Always implement idempotent webhook handlers using the x-unizo-delivery-id header:

Idempotent Webhook Handler

// Example: Handling webhooks idempotently
app.post('/webhooks/communications', async (req, res) => {
const deliveryId = req.headers['x-unizo-delivery-id'];

// Check if we've already processed this delivery
const existingDelivery = await db.webhookDeliveries.findOne({ 
  deliveryId 
});

if (existingDelivery) {
  console.log(`Duplicate delivery detected: ${deliveryId}`);
  return res.status(200).json({ 
    status: 'already_processed' 
  });
}

// Process the webhook
try {
  await processWebhook(req.body);
  
  // Record the delivery
  await db.webhookDeliveries.create({
    deliveryId,
    processedAt: new Date()
  });
  
  res.status(200).json({ status: 'success' });
} catch (error) {
  console.error('Webhook processing failed:', error);
  res.status(500).json({ status: 'error' });
}
});

2. Async Processing

For better performance and reliability, process webhooks asynchronously:

Async Webhook Processing

// Example: Queue webhooks for async processing
app.post('/webhooks/communications', async (req, res) => {
const { type, data } = req.body;

// Immediately acknowledge receipt
res.status(200).json({ status: 'received' });

// Queue for async processing
await messageQueue.publish('webhook.communications', {
  type,
  data,
  headers: {
    webhookId: req.headers['x-unizo-webhook-id'],
    deliveryId: req.headers['x-unizo-delivery-id'],
    signature: req.headers['x-unizo-signature']
  },
  receivedAt: new Date()
});
});

// Background worker
messageQueue.subscribe('webhook.communications', async (message) => {
const { type, data } = message;

switch (type) {
  case 'message:sent':
    await handleNewMessage(data.message);
    break;
  case 'channel:created':
    await handleNewChannel(data.channel);
    break;
  // ... other event types
}
});

3. Error Handling

Implement comprehensive error handling and monitoring:

Error Handling

// Example: Robust error handling
app.post('/webhooks/communications', async (req, res) => {
try {
  // Validate webhook signature
  if (!validateWebhookSignature(req)) {
    return res.status(401).json({ 
      error: 'Invalid signature' 
    });
  }
  
  // Validate payload structure
  const { error } = validateWebhookPayload(req.body);
  if (error) {
    return res.status(400).json({ 
      error: 'Invalid payload',
      details: error.details 
    });
  }
  
  // Process webhook
  await processWebhook(req.body);
  res.status(200).json({ status: 'success' });
  
} catch (error) {
  // Log error with context
  logger.error('Webhook processing failed', {
    error: error.message,
    stack: error.stack,
    webhookType: req.body.type,
    integrationId: req.body.integration?.id,
    deliveryId: req.headers['x-unizo-delivery-id']
  });
  
  // Return 500 to trigger retry
  res.status(500).json({ 
    error: 'Internal server error' 
  });
}
});

Need Help?

For webhook-related support: