Skip to main content

Packages And Container Registry Webhooks

Unizo's Package Registry API provides webhooks to notify your application when important events occur in your package registries and artifact repositories. These real-time notifications enable you to build automated CI/CD workflows, maintain artifact synchronization, track package usage, and ensure security compliance.

Our platform normalizes webhook events from various package registry providers (JFrog Artifactory, Nexus Repository, Docker Registry, GitHub Packages, etc.) into a consistent format, making it easy to handle artifact events across different registry platforms.

Webhook Configuration

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

Supported Event Types

Event TypeDescriptionTrigger Conditions
artifact:createdEvent broadcast when an artifact has been createdNew artifact created
artifact:deletedEvent broadcast when an artifact is deletedExisting artifact gets deleted

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')
  );
}

Artifact Events

Artifact Created

artifact:created

Triggered when a new artifact, package, or container image is uploaded to your package registry.
POSThttps://api.yourapp.com/webhooks/unizo/pcr
Headers
NameTypeRequiredDescription
x-unizo-event-typestringYesThe type of event that was triggered
x-unizo-signaturestringYesHMAC SHA-256 signature
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
contentTypestringYesBranch name/identifier
artifactstringYesArtifact Name
artifact.idstringYesArtifact ID
artifact.keystringYesArtifact Key
artifact.namestringYesArtifact Name
artifact.pathstringYesArtifact Path
integration.typestringYesIntegration type
integration.idstringYesIntegration ID
integration.namestringYesIntegration display name
Example Payload
{
"type": "artifact:created",
"version": "1.0.0",
"contentType": "application/json",
"artifact": {
"id": "testing",
"key": "testing-webhook",
"name": "manifest.json",
"path": "unizoinc/otg-event-catcher/v1.0.0/manifest.json"
},
"integration": {
"type": "PCR",
"id": "864e88c4-69ce-4df7-a2a1-ab3c4e439fac",
"name": "JF_Secu_1734358143372"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Artifact Deleted

artifact:deleted

Triggered when an artifact is removed from the package registry.
POSThttps://api.yourapp.com/webhooks/unizo/pcr
Headers
NameTypeRequiredDescription
x-unizo-event-typestringYesThe type of event that was triggered
x-unizo-signaturestringYesHMAC SHA-256 signature
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
contentTypestringYesBranch name/identifier
artifactstringYesArtifact Name
artifact.idstringYesArtifact ID
artifact.keystringYesArtifact Key
artifact.pathstringYesArtifact Path
artifact.namestringYesArtifact Name
integration.typestringYesIntegration type
integration.idstringYesIntegration ID
integration.namestringYesIntegration display name
Example Payload
{
"type": "artifact:deleted",
"version": "1.0.0",
"contentType": "application/json",
"artifact": {
"id": "docker-trial",
"key": "docker-trial-webhook",
"path": "exposures/latest/manifest.json",
"name": "manifest.json"
},
"integration": {
"type": "PCR",
"id": "be03cab2-e9d2-4d95-ad45-c5411b2be10a",
"name": "JF_Secu_1734088106577"
}
}
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/pcr', 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 processArtifactEvent(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. CI/CD Integration

Integrate package events into your CI/CD pipeline:

CI/CD Pipeline Integration

// Example: Trigger deployments on new releases
app.post('/webhooks/pcr', async (req, res) => {
const { type, data } = req.body;

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

switch (type) {
  case 'version:released':
    // Trigger deployment pipeline
    if (data.release.version.match(/^d+.d+.0$/)) {
      // Major or minor release - deploy to staging
      await cicdApi.triggerPipeline({
        pipeline: 'deploy-staging',
        parameters: {
          package: data.package.name,
          version: data.release.version,
          artifacts: data.release.artifacts
        }
      });
      
      // Notify team
      await slackApi.postMessage({
        channel: '#releases',
        text: `🚀 New release: ${data.package.name} v${data.release.version}`,
        attachments: [{
          title: 'Release Notes',
          text: data.release.release_notes
        }]
      });
    }
    break;
    
  case 'scan:completed':
    // Check security policy
    if (data.policy_status === 'fail') {
      // Block deployment
      await cicdApi.failPipeline({
        buildId: data.artifact.id,
        reason: 'Security scan failed',
        details: `Found ${data.vulnerabilities.critical} critical vulnerabilities`
      });
      
      // Create security ticket
      await jiraApi.createIssue({
        type: 'Security',
        priority: 'Critical',
        summary: `Security vulnerabilities in ${data.artifact.name}:${data.artifact.version}`,
        description: formatVulnerabilities(data.vulnerabilities)
      });
    }
    break;
}
});

3. Artifact Tracking and Compliance

Maintain artifact inventory and compliance records:

Artifact Compliance Tracking

// Example: Track artifacts for compliance
app.post('/webhooks/pcr', async (req, res) => {
const { type, data, integration } = req.body;

// Create audit record
const auditEntry = {
  timestamp: new Date(),
  eventType: type,
  integration: integration.name,
  artifact: {
    id: data.artifact?.id,
    name: data.artifact?.name,
    version: data.artifact?.version,
    type: data.artifact?.type
  }
};

switch (type) {
  case 'artifact:created':
    // Register in artifact inventory
    await inventoryDb.artifacts.create({
      ...data.artifact,
      status: 'active',
      compliance: {
        scanned: false,
        approved: false,
        licenses: []
      }
    });
    
    // Trigger compliance checks
    await complianceApi.scanArtifact({
      artifactId: data.artifact.id,
      checks: ['license', 'security', 'quality']
    });
    break;
    
  case 'vulnerability:detected':
    // Update compliance status
    await inventoryDb.artifacts.update(data.artifact.id, {
      compliance: {
        scanned: true,
        securityStatus: data.policy_status,
        vulnerabilities: {
          critical: data.vulnerabilities.critical,
          high: data.vulnerabilities.high,
          lastScan: data.scan.completed_at
        }
      }
    });
    
    // Generate compliance report
    if (data.vulnerabilities.critical > 0) {
      await reportingApi.generateSecurityReport({
        artifact: data.artifact,
        vulnerabilities: data.vulnerabilities,
        recipients: ['security-team@company.com']
      });
    }
    break;
}

// Store audit trail
await auditDb.packageEvents.create(auditEntry);

res.status(200).json({ status: 'tracked' });
});

Need Help?

For webhook-related support: