Skip to main content

Platform Webhooks

Platform webhooks enable your applications to receive real-time notifications when integration lifecycle events occur in Unizo.
They help you stay synchronized with configuration changes and automate downstream updates without polling.

Unizo sends webhook notifications for key integration events such as creation, update, and deletion—standardized across all connected systems.

Webhook Configuration

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

Supported Event Types

These are the event types currently supported by Unizo’s Platform Webhooks.
Each event delivers normalized metadata so that your webhook handlers can process events uniformly.

Event TypeDescriptionTrigger Conditions
integration:createdTriggered when a new integration is successfully created in Unizo.Integration Created
integration:updatedTriggered when an existing integration configuration is updated.Integration Updated
integration:deletedTriggered when an integration is disconnected or removed from Unizo.Integration Deleted

Webhook Security

Every webhook request sent by Unizo includes a cryptographic signature so you can verify that the payload is authentic and has not been tampered with.

Security Headers

HeaderDescription
x-unizo-event-typeThe type of event that triggered the webhook
x-unizo-signatureHMAC-SHA256 signature of the payload, prefixed with v1= (e.g., v1=ee084789...)
x-unizo-timestampUnix epoch timestamp (seconds) when the request was signed
x-unizo-delivery-idUnique identifier for this webhook delivery

Signature Verification

The signed payload is constructed by joining the timestamp and the raw request body with a dot separator: {timestamp}.{payload}. This ensures the timestamp is covered by the signature, preventing replay attacks.

Verification Steps

  1. Parse the timestamp from the x-unizo-timestamp header and reject the request if it falls outside your tolerance window (recommended: 5 minutes).
  2. Reconstruct the signed payload by concatenating the timestamp, a literal dot (.), and the raw request body.
  3. Compute the expected signature using HMAC-SHA256 with your webhook signing secret as the key.
  4. Strip the v1= prefix from the x-unizo-signature header to get the received signature.
  5. Compare the two signatures using a constant-time comparison function to prevent timing attacks.

Reference Implementation

const crypto = require("crypto");

function verifyWebhookSignature(
  payload,
  signatureHeader,
  timestampHeader,
  secret,
  toleranceSeconds = 300
) {
  // 1. Reject stale timestamps to prevent replay attacks
  const now = Math.floor(Date.now() / 1000);
  const timestamp = parseInt(timestampHeader, 10);

  if (Number.isNaN(timestamp)) {
    return false;
  }

  if (Math.abs(now - timestamp) > toleranceSeconds) {
    return false;
  }

  // 2. Reconstruct the signed payload: "{timestamp}.{payload}"
  const signedPayload = `${timestamp}.${payload}`;

  // 3. Compute the expected signature
  const expected = crypto
    .createHmac("sha256", secret)
    .update(signedPayload)
    .digest("hex");

  // 4. Strip the "v1=" prefix from the received signature
  const received = signatureHeader.startsWith("v1=")
    ? signatureHeader.slice(3)
    : signatureHeader;

  // 5. Constant-time comparison to prevent timing attacks
  try {
    return crypto.timingSafeEqual(
      Buffer.from(expected, "hex"),
      Buffer.from(received, "hex")
    );
  } catch {
    return false;
  }
}

// Usage
const isValid = verifyWebhookSignature(
  rawBody,                                // raw request body string
  headers["x-unizo-signature"],           // "v1=ee084789..."
  headers["x-unizo-timestamp"],           // "1774093147"
  process.env.UNIZO_WEBHOOK_SECRET        // your signing secret
);

if (!isValid) {
  return res.status(401).json({ error: "Invalid signature" });
}

// Signature verified — process the event
handleEvent(JSON.parse(rawBody));

Integration Events

Integration Created

integration:created

Triggered when a new integration is created and connected in Unizo.
POSThttps://api.yourapp.com/webhooks/unizo/platform
Headers
NameTypeRequiredDescription
x-unizo-event-typestringYesType of event triggered
x-unizo-signaturestringYesHMAC SHA-256 signature for payload verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
contentTypestringYesContent type of the webhook payload
integration.idstringYesUnique identifier for the integration
integration.namestringYesDisplay name of the integration
integration.typestringYesIntegration category, e.g., SCM, HRIS, ITSM
integration.statusstringYesCurrent status of the integration (active, inactive, pending)
organization.idstringYesUnique identifier of the associated organization
organization.keystringYesOrganization key or slug used within Unizo
createdDateTimestringYesTimestamp when the integration was created
Example Payload
{
"type": "integration:created",
"version": "1.0.0",
"contentType": "application/json",
"integration": {
"id": "4f56a3e2-18c2-441a-b95e-c64bde1832a2",
"name": "Freshworks-GRC",
"type": "ITSM",
"status": "active"
},
"organization": {
"id": "167400081",
"key": "unizo-platform"
},
"createdDateTime": "2025-05-05T18:22:10Z"
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Integration Updated

integration:updated

Triggered when configuration or credentials of an existing integration are modified.
POSThttps://api.yourapp.com/webhooks/unizo/platform
Headers
NameTypeRequiredDescription
x-unizo-event-typestringYesType of event triggered
x-unizo-signaturestringYesHMAC SHA-256 signature for payload verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
contentTypestringYesContent type of the webhook payload
integration.idstringYesUnique identifier for the integration
integration.namestringYesDisplay name of the integration
integration.typestringYesIntegration category, e.g., SCM, HRIS, ITSM
integration.statusstringYesCurrent status of the integration
integration.previous_stateobjectNoPrevious configuration before update
integration.updatedDateTimestringYesTimestamp of the update
Example Payload
{
"type": "integration:updated",
"version": "1.0.0",
"contentType": "application/json",
"integration": {
"id": "4f56a3e2-18c2-441a-b95e-c64bde1832a2",
"name": "Freshworks-GRC",
"type": "ITSM",
"status": "active",
"previous_state": {
"status": "inactive"
},
"updatedDateTime": "2025-05-07T09:42:31Z"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Integration Deleted

integration:deleted

Triggered when an integration is disconnected or permanently deleted from Unizo.
POSThttps://api.yourapp.com/webhooks/unizo/platform
Headers
NameTypeRequiredDescription
x-unizo-event-typestringYesType of event triggered
x-unizo-signaturestringYesHMAC SHA-256 signature for payload verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
contentTypestringYesContent type of the webhook payload
integration.idstringYesUnique identifier for the integration
integration.namestringYesDisplay name of the integration
integration.typestringYesIntegration category, e.g., SCM, HRIS, ITSM
organization.idstringYesUnique identifier of the associated organization
deletedDateTimestringYesTimestamp when the integration was deleted
Example Payload
{
"type": "integration:deleted",
"version": "1.0.0",
"contentType": "application/json",
"integration": {
"id": "4f56a3e2-18c2-441a-b95e-c64bde1832a2",
"name": "Freshworks-GRC",
"type": "ITSM"
},
"organization": {
"id": "167400081"
},
"deletedDateTime": "2025-05-09T11:10:12Z"
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Webhook Delivery & Retries

Unizo implements automatic retry logic for failed webhook deliveries:

  1. Initial Delivery: Immediate
  2. First Retry: After 1 minute
  3. Second Retry: After 5 minutes
  4. Third Retry: After 15 minutes
  5. Final Retry: After 1 hour

A webhook delivery is marked as failed if:

  • Your endpoint returns a non-2xx status code
  • Connection timeout (30 seconds)
  • SSL/TLS handshake errors

Best Practices

1. Idempotency

Idempotent Webhook Handler

async function handleWebhook(request) {
const deliveryId = request.headers['x-unizo-delivery-id'];

// Skip if already processed
if (await isProcessed(deliveryId)) {
return { status: 200, message: 'Already processed' };
}

await processWebhook(request.body);
await markProcessed(deliveryId);

return { status: 200 };
}

2. Asynchronous Processing

Queue Webhook for Async Processing

app.post('/webhooks/platform', (req, res) => {
if (!verifySignature(req)) {
  return res.status(401).send('Invalid signature');
}

webhookQueue.add(req.body);
res.status(200).send('OK');
});

3. Error Handling

Webhook Error Handling Example

async function processWebhook(payload) {
try {
  // Business logic
  await updateIntegrationState(payload);
} catch (err) {
  console.error('Webhook processing failed:', err);
  throw err;
}
}

Troubleshooting

Common Issues

    Webhook Logs

    Access detailed webhook logs in the Unizo Console:

    1. Navigate to your integration
    2. Click on "Webhooks" tab
    3. View delivery history with request/response details

    Need Help?

    For webhook-related support: