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
Create Webhook Endpoint
Set up an HTTPS endpoint on your server to receive POST requests
Register Webhook
Tell Brand DNA which events to send to your endpoint
Receive Events
Brand DNA sends HTTP POST with event data when events occur
Respond with 200 OK
Acknowledge receipt within 5 seconds
Available Events
Event Description Trigger analysis.startedBrand analysis began processing Analysis created via API or web analysis.completedBrand analysis finished successfully Analysis processing complete analysis.failedBrand analysis failed with error Analysis encountered error export.readyPDF export ready for download Export generation complete competitor.changedTracked competitor changed Competitor website updated team.member_addedNew team member joined Invitation accepted team.member_removedTeam member removed Member removed from account subscription.updatedSubscription plan changed Upgrade/downgrade subscription.canceledSubscription canceled User 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
Parameter Type Required Description urlstring Yes HTTPS endpoint URL (must be publicly accessible) eventsarray Yes Array of event types to subscribe to descriptionstring No Webhook description (max 200 chars) secretstring No Custom 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
}
}
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:
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:
Retry #1: After 1 minute
Retry #2: After 5 minutes
Retry #3: After 30 minutes
Retry #4: After 2 hours
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
Related Pages
Webhooks configured! Return to API Overview for more endpoints.