Skip to Content
DashboardWebhooks

Webhooks

Webhooks send HTTP POST requests to your server when builds, submissions, or updates complete.

Events

EventDescription
build_completedBuild finished successfully
build_failedBuild failed
submit_completedStore submission succeeded
submit_failedStore submission failed
update_completedOTA update published
update_failedOTA update failed
allSubscribe to all events

Creating Webhooks

From Dashboard

  1. Go to Dashboard → Settings → Webhooks
  2. Click Create Webhook
  3. Enter:
    • Name: Friendly name (e.g., “Slack Notifications”)
    • URL: HTTPS endpoint (must be HTTPS)
    • Events: Select events to subscribe to
    • Project filter: Optional, comma-separated project names
  4. Click Create
  5. Copy the signing secret. It’s only shown once

Payload Format

Webhooks receive JSON payloads:

{ "event": "build_completed", "timestamp": "2024-01-15T10:30:00.000Z", "data": { "id": "build-1234567890", "projectName": "my-app", "platform": "ios", "status": "success", "version": "1.0.0", "buildNumber": "42", "buildType": "release", "configuration": "prod", "distributionType": "appstore", "organizationId": "org_abc123" } }

Payload Fields

FieldTypeDescription
idstringBuild, submit, or update ID
projectNamestringProject name
platformstringPlatform: ios, android, or visionos
statusstringResult status: success or failed
versionstringApp version
buildNumberstringBuild number
buildTypestringBuild type: debug or release
configurationstringDeployment target (e.g., prod, stg, dev)
distributionTypestringiOS distribution: appstore, adhoc, enterprise
organizationIdstringOrganization ID

Signature Verification

Webhooks include an HMAC-SHA256 signature for verification:

X-Norrix-Signature: sha256=<hex_signature>

Verification Example (Node.js)

const crypto = require('crypto'); function verifyWebhookSignature(payload, signature, secret) { const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(payload, 'utf8').digest('hex'); return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); } // Express middleware app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-norrix-signature']; const payload = req.body.toString(); if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); } const event = JSON.parse(payload); // Handle event... res.status(200).send('OK'); });

Project Filtering

Filter webhooks to specific projects:

Include Projects

Comma-separated list of project names to include:

my-app, other-app

Only events from these projects trigger the webhook.

Exclude Projects

Comma-separated list of project names to exclude:

test-app, experimental

Events from these projects are skipped.


Managing Webhooks

View Webhooks

Dashboard → Settings → Webhooks shows:

  • Name
  • URL
  • Events
  • Enabled status
  • Last triggered
  • Last status code

Edit Webhook

  1. Click on the webhook
  2. Update fields
  3. Save changes

Regenerate Secret

  1. Edit the webhook
  2. Click Regenerate Secret
  3. Copy new secret
  4. Update your server

Disable/Enable

Toggle the Enabled switch to temporarily disable a webhook without deleting it.

Delete Webhook

  1. Click Delete
  2. Confirm deletion

Delivery history is also deleted.


Delivery History

View recent deliveries for each webhook:

  • Timestamp
  • Event type
  • HTTP status code
  • Response time
  • Error message (if failed)

Retry Failed Deliveries

Currently, failed deliveries aren’t automatically retried. Configure your endpoint to be idempotent.


Best Practices

Respond Quickly

Return 200 OK within 10 seconds. Process events asynchronously:

app.post('/webhook', (req, res) => { // Acknowledge receipt immediately res.status(200).send('OK'); // Process asynchronously processEventAsync(req.body); });

Idempotency

Events may be delivered multiple times. Use event ID for deduplication:

async function handleEvent(event) { if (await isProcessed(event.data.id)) { return; // Already handled } // Process event await markProcessed(event.data.id); }

Error Handling

Log failures for debugging:

app.post('/webhook', async (req, res) => { try { await processEvent(req.body); res.status(200).send('OK'); } catch (error) { console.error('Webhook error:', error); res.status(500).send('Error'); } });

Security

  • Always verify signatures
  • Use HTTPS endpoints
  • Keep secrets secure
  • Rotate secrets if compromised