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)

saas packs v1.0.0
View Plugin

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.

Skill file: plugins/saas-packs/juicebox-pack/skills/juicebox-webhooks-events/SKILL.md