Skip to main content

Overview

Webhooks enable you to receive real-time notifications when events occur in Brand DNA. Instead of polling the API, Brand DNA sends HTTP POST requests to your server when events happen. Base Endpoint: /v1/webhooks Required Scopes:
  • admin:full - Manage webhooks

How Webhooks Work

1

Create Webhook Endpoint

Set up an HTTPS endpoint on your server to receive POST requests
2

Register Webhook

Tell Brand DNA which events to send to your endpoint
3

Receive Events

Brand DNA sends HTTP POST with event data when events occur
4

Respond with 200 OK

Acknowledge receipt within 5 seconds

Available Events

EventDescriptionTrigger
analysis.startedBrand analysis began processingAnalysis created via API or web
analysis.completedBrand analysis finished successfullyAnalysis processing complete
analysis.failedBrand analysis failed with errorAnalysis encountered error
export.readyPDF export ready for downloadExport generation complete
competitor.changedTracked competitor changedCompetitor website updated
team.member_addedNew team member joinedInvitation accepted
team.member_removedTeam member removedMember removed from account
subscription.updatedSubscription plan changedUpgrade/downgrade
subscription.canceledSubscription canceledUser canceled plan

Create Webhook

Register a new webhook endpoint.

POST /v1/webhooks

curl https://api.branddna.app/v1/webhooks \
  -X POST \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/brand-dna",
    "events": ["analysis.completed", "export.ready"],
    "description": "Production server webhook"
  }'

Request Parameters

ParameterTypeRequiredDescription
urlstringYesHTTPS endpoint URL (must be publicly accessible)
eventsarrayYesArray of event types to subscribe to
descriptionstringNoWebhook description (max 200 chars)
secretstringNoCustom signing secret (auto-generated if omitted)

Response

{
  "data": {
    "id": "whk_xyz789",
    "url": "https://your-server.com/webhooks/brand-dna",
    "events": ["analysis.completed", "export.ready"],
    "description": "Production server webhook",
    "secret": "whsec_abc123def456...",
    "status": "active",
    "created_at": "2026-01-27T14:23:45Z"
  }
}
Save the secret! It’s shown only once and used to verify webhook signatures.

List Webhooks

Get all registered webhooks.

GET /v1/webhooks

curl https://api.branddna.app/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    {
      "id": "whk_xyz789",
      "url": "https://your-server.com/webhooks/brand-dna",
      "events": ["analysis.completed", "export.ready"],
      "status": "active",
      "last_delivery": "2026-01-27T14:20:00Z",
      "success_rate": 98.5,
      "created_at": "2026-01-20T10:00:00Z"
    }
  ]
}
Status Values:
  • active - Webhook enabled
  • paused - Webhook disabled (manually or due to failures)
  • failed - Webhook permanently failed (5+ consecutive failures)

Delete Webhook

Unregister a webhook.

DELETE /v1/webhooks/:webhook_id

curl https://api.branddna.app/v1/webhooks/whk_xyz789 \
  -X DELETE \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "id": "whk_xyz789",
    "deleted": true
  }
}

Webhook Payload Format

All webhooks send JSON payloads with this structure:
{
  "id": "evt_abc123",
  "event": "analysis.completed",
  "created": 1706368800,
  "data": {
    "analysis_id": "anl_xyz789",
    "url": "https://example.com",
    "type": "full",
    "status": "completed",
    "results_url": "https://api.branddna.app/v1/analyses/anl_xyz789"
  }
}

Event Payloads

analysis.completed

{
  "id": "evt_001",
  "event": "analysis.completed",
  "created": 1706368800,
  "data": {
    "analysis_id": "anl_xyz789",
    "url": "https://example.com",
    "type": "full",
    "status": "completed",
    "completed_at": "2026-01-27T14:24:12Z",
    "results_url": "https://api.branddna.app/v1/analyses/anl_xyz789"
  }
}

export.ready

{
  "id": "evt_002",
  "event": "export.ready",
  "created": 1706368900,
  "data": {
    "export_id": "exp_abc123",
    "analysis_id": "anl_xyz789",
    "format": "pdf",
    "download_url": "https://cdn.branddna.app/exports/exp_abc123.pdf",
    "expires_at": "2026-02-03T14:23:45Z"
  }
}

competitor.changed

{
  "id": "evt_003",
  "event": "competitor.changed",
  "created": 1706369000,
  "data": {
    "competitor_id": "cmp_xyz789",
    "competitor_name": "Competitor Inc.",
    "url": "https://competitor.com",
    "change": {
      "type": "headline_change",
      "field": "headline",
      "old_value": "Best Software",
      "new_value": "Future of Work"
    }
  }
}

Verifying Webhook Signatures

All webhooks include a signature to verify authenticity:

Signature Header

X-Webhook-Signature: t=1706368800,v1=5f7e8a9b...
Format: t=timestamp,v1=signature
  • t - Unix timestamp when signature was generated
  • v1 - HMAC SHA-256 signature

Verification (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  // Parse signature header
  const elements = signature.split(',');
  const timestamp = elements[0].split('=')[1];
  const sig = elements[1].split('=')[1];

  // Create signed payload
  const signedPayload = `${timestamp}.${payload}`;

  // Compute expected signature
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Compare signatures
  return crypto.timingSafeEqual(
    Buffer.from(sig),
    Buffer.from(expectedSig)
  );
}

// Express.js example
app.post('/webhooks/brand-dna', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const secret = process.env.WEBHOOK_SECRET;

  const payload = JSON.stringify(req.body);

  if (!verifyWebhookSignature(payload, signature, secret)) {
    return res.status(401).send('Invalid signature');
  }

  // Process webhook
  const event = req.body;
  console.log('Received event:', event.event);

  // Respond quickly
  res.status(200).send('OK');
});

Verification (Python)

import hmac
import hashlib

def verify_webhook_signature(payload, signature, secret):
    # Parse signature header
    elements = dict(item.split('=') for item in signature.split(','))
    timestamp = elements['t']
    sig = elements['v1']

    # Create signed payload
    signed_payload = f"{timestamp}.{payload}"

    # Compute expected signature
    expected_sig = hmac.new(
        secret.encode('utf-8'),
        signed_payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    # Compare signatures (timing-safe)
    return hmac.compare_digest(sig, expected_sig)
Always verify signatures to prevent spoofed webhooks!

Responding to Webhooks

Success Response

Return 200 OK within 5 seconds:
app.post('/webhooks/brand-dna', async (req, res) => {
  // Respond immediately
  res.status(200).send('OK');

  // Process asynchronously
  processWebhookAsync(req.body);
});

Failure Handling

If your endpoint returns non-200 status or times out:
  1. Retry #1: After 1 minute
  2. Retry #2: After 5 minutes
  3. Retry #3: After 30 minutes
  4. Retry #4: After 2 hours
  5. Retry #5: After 6 hours
After 5 failures: Webhook marked as failed and disabled
Process webhooks asynchronously and respond quickly to avoid timeouts!

Best Practices

Always verify X-Webhook-Signature to ensure webhooks are from Brand DNA.
Use event id to detect and ignore duplicate deliveries (can happen during retries).
Return 200 OK within 5 seconds. Process events asynchronously in background jobs.
Webhook URLs must use HTTPS. HTTP endpoints are rejected for security.
Set up alerts for failed webhooks. Check success_rate regularly.

Testing Webhooks

Test Event

Trigger a test webhook delivery:

POST /v1/webhooks/:webhook_id/test

curl https://api.branddna.app/v1/webhooks/whk_xyz789/test \
  -X POST \
  -H "Authorization: Bearer YOUR_API_KEY"
Sends:
{
  "id": "evt_test_001",
  "event": "webhook.test",
  "created": 1706368800,
  "data": {
    "message": "This is a test webhook"
  }
}

Webhook Logs

View recent deliveries and responses:

GET /v1/webhooks/:webhook_id/deliveries

curl https://api.branddna.app/v1/webhooks/whk_xyz789/deliveries \
  -H "Authorization: Bearer YOUR_API_KEY"
Response:
{
  "data": [
    {
      "id": "del_001",
      "event_id": "evt_abc123",
      "event_type": "analysis.completed",
      "status": "success",
      "response_code": 200,
      "response_time_ms": 145,
      "attempted_at": "2026-01-27T14:20:00Z"
    },
    {
      "id": "del_002",
      "event_id": "evt_def456",
      "event_type": "export.ready",
      "status": "failed",
      "response_code": 500,
      "error": "Connection timeout",
      "attempted_at": "2026-01-27T14:15:00Z",
      "retry_count": 2
    }
  ]
}

Use Cases

Slack Notifications

Send team alerts when analyses complete

Client Reporting

Auto-email PDF reports to clients

Database Sync

Update internal systems when data changes

Analytics Pipeline

Stream events to data warehouse
Webhooks configured! Return to API Overview for more endpoints.