juicebox-webhooks-events
Implement Juicebox webhook handling. Use when setting up event notifications, processing webhooks, or integrating real-time updates from Juicebox. Trigger with phrases like "juicebox webhooks", "juicebox events", "juicebox notifications", "juicebox real-time". allowed-tools: Read, Write, Edit, Bash(gh:*), Bash(curl:*) version: 1.0.0 license: MIT author: Jeremy Longshore <jeremy@intentsolutions.io>
Allowed Tools
No tools specified
Provided by Plugin
juicebox-pack
Claude Code skill pack for Juicebox (24 skills)
Installation
This skill is included in the juicebox-pack plugin:
/plugin install juicebox-pack@claude-code-plugins-plus
Click to copy
Instructions
# Juicebox Webhooks & Events
## Overview
Implement webhook handlers for real-time Juicebox events and notifications.
## Prerequisites
- Juicebox account with webhooks enabled
- HTTPS endpoint for webhook delivery
- Request signature verification capability
## Instructions
### Step 1: Register Webhook Endpoint
```typescript
// First, configure in Juicebox dashboard or via API
import { JuiceboxClient } from '@juicebox/sdk';
const client = new JuiceboxClient({
apiKey: process.env.JUICEBOX_API_KEY!
});
await client.webhooks.create({
url: 'https://your-app.com/webhooks/juicebox',
events: [
'search.completed',
'profile.enriched',
'export.ready',
'quota.warning'
],
secret: process.env.JUICEBOX_WEBHOOK_SECRET
});
```
### Step 2: Implement Webhook Handler
```typescript
// routes/webhooks.ts
import { Router } from 'express';
import crypto from 'crypto';
const router = Router();
// Verify webhook signature
function verifySignature(payload: string, signature: string, secret: string): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}
router.post('/webhooks/juicebox', express.raw({ type: 'application/json' }), async (req, res) => {
const signature = req.headers['x-juicebox-signature'] as string;
const payload = req.body.toString();
// Verify signature
if (!verifySignature(payload, signature, process.env.JUICEBOX_WEBHOOK_SECRET!)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(payload);
// Acknowledge receipt immediately
res.status(200).json({ received: true });
// Process event asynchronously
await processWebhookEvent(event);
});
export default router;
```
### Step 3: Process Different Event Types
```typescript
// services/webhook-processor.ts
interface WebhookEvent {
id: string;
type: string;
timestamp: string;
data: any;
}
export async function processWebhookEvent(event: WebhookEvent): Promise {
console.log(`Processing event: ${event.type} (${event.id})`);
switch (event.type) {
case 'search.completed':
await handleSearchCompleted(event.data);
break;
case 'profile.enriched':
await handleProfileEnriched(event.data);
break;
case 'export.ready':
await handleExportReady(event.data);
break;
case 'quota.warning':
await handleQuotaWarning(event.data);
break;
default:
console.warn(`Unknown event type: ${event.type}`);
}
}
async function handleSearchCompleted(data: { searchId: string; resultCount: number }) {
// Notify user that search is complete
await notificationService.send({
type: 'search_complete',
searchId: data.searchId,
message: `Search completed with ${data.resultCount} results`
});
}
async function handleProfileEnriched(data: { profileId: string; fields: string[] }) {
// Update local cache with enriched data
await cacheService.invalidate(`profile:${data.profileId}`);
await db.profiles.update({
where: { id: data.profileId },
data: { enrichedAt: new Date() }
});
}
async function handleExportReady(data: { exportId: string; downloadUrl: string }) {
// Notify user and store download URL
await notificationService.send({
type: 'export_ready',
exportId: data.exportId,
downloadUrl: data.downloadUrl
});
}
async function handleQuotaWarning(data: { usage: number; limit: number }) {
// Alert team about quota usage
const percentage = (data.usage / data.limit) * 100;
if (percentage > 80) {
await alertService.send({
severity: 'warning',
message: `Juicebox quota at ${percentage.toFixed(1)}%`
});
}
}
```
### Step 4: Implement Retry Logic
```typescript
// lib/webhook-queue.ts
import { Queue } from 'bullmq';
const webhookQueue = new Queue('juicebox-webhooks', {
connection: { host: 'localhost', port: 6379 }
});
export async function queueWebhookProcessing(event: WebhookEvent): Promise {
await webhookQueue.add('process', event, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 1000
}
});
}
// Worker
import { Worker } from 'bullmq';
new Worker('juicebox-webhooks', async (job) => {
await processWebhookEvent(job.data);
}, {
connection: { host: 'localhost', port: 6379 }
});
```
## Webhook Events Reference
| Event | Description | Payload |
|-------|-------------|---------|
| `search.completed` | Async search finished | searchId, resultCount |
| `profile.enriched` | Profile data enriched | profileId, fields |
| `export.ready` | Bulk export ready | exportId, downloadUrl |
| `quota.warning` | Approaching quota limit | usage, limit |
| `key.rotated` | API key rotated | newKeyPrefix |
## Output
- Webhook endpoint handler
- Signature verification
- Event type processors
- Retry queue with backoff
## Error Handling
| Issue | Cause | Solution |
|-------|-------|----------|
| Invalid signature | Wrong secret | Verify webhook secret |
| Duplicate events | Network retry | Implement idempotency |
| Processing timeout | Slow handler | Use async queue |
## Resources
- [Webhooks Documentation](https://juicebox.ai/docs/webhooks)
- [Event Reference](https://juicebox.ai/docs/events)
## Next Steps
After webhooks, see `juicebox-performance-tuning` for optimization.