Webhooks API

Configure and manage webhooks for real-time event notifications.

Endpoints#

MethodEndpointDescription
GET/webhooksList webhook endpoints
POST/webhooksCreate a webhook
GET/webhooks/:idGet webhook details
PATCH/webhooks/:idUpdate a webhook
DELETE/webhooks/:idDelete a webhook
GET/webhooks/:id/deliveriesList delivery attempts
POST/webhooks/:id/testSend test event

Webhook Object#

1{ 2 "id": "wh_abc123", 3 "url": "https://example.com/webhooks/bootspring", 4 "events": ["project.created", "agent.invoked"], 5 "status": "active", 6 "secret": "whsec_xxx...", 7 "metadata": { 8 "environment": "production" 9 }, 10 "stats": { 11 "deliveriesTotal": 156, 12 "deliveriesSucceeded": 152, 13 "deliveriesFailed": 4, 14 "lastDeliveryAt": "2024-01-15T10:30:00Z" 15 }, 16 "createdAt": "2024-01-01T00:00:00Z" 17}

Available Events#

Project Events#

EventDescription
project.createdNew project created
project.updatedProject settings updated
project.deletedProject deleted

Agent Events#

EventDescription
agent.invokedAgent was invoked
agent.completedAgent completed task
agent.failedAgent invocation failed

Skill Events#

EventDescription
skill.appliedSkill pattern applied
skill.failedSkill application failed

Usage Events#

EventDescription
usage.threshold_reachedUsage threshold reached
usage.limit_exceededUsage limit exceeded

Subscription Events#

EventDescription
subscription.createdSubscription started
subscription.updatedSubscription changed
subscription.canceledSubscription canceled
invoice.paidInvoice payment succeeded
invoice.payment_failedInvoice payment failed

List Webhooks#

GET /webhooks

Example Request#

curl https://api.bootspring.dev/v1/webhooks \ -H "Authorization: Bearer bs_xxx"

Response#

1{ 2 "data": [ 3 { 4 "id": "wh_abc123", 5 "url": "https://example.com/webhooks/bootspring", 6 "events": ["project.created", "agent.invoked"], 7 "status": "active", 8 "createdAt": "2024-01-01T00:00:00Z" 9 } 10 ] 11}

Create Webhook#

POST /webhooks

Request Body#

FieldTypeRequiredDescription
urlstringYesHTTPS endpoint URL
eventsarrayYesEvents to subscribe to
metadataobjectNoCustom metadata

Example Request#

1curl -X POST https://api.bootspring.dev/v1/webhooks \ 2 -H "Authorization: Bearer bs_xxx" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "url": "https://example.com/webhooks/bootspring", 6 "events": ["project.created", "agent.invoked", "usage.threshold_reached"], 7 "metadata": { 8 "environment": "production" 9 } 10 }'

Response#

1{ 2 "data": { 3 "id": "wh_def456", 4 "url": "https://example.com/webhooks/bootspring", 5 "events": ["project.created", "agent.invoked", "usage.threshold_reached"], 6 "status": "active", 7 "secret": "whsec_abc123xyz...", 8 "metadata": { 9 "environment": "production" 10 }, 11 "createdAt": "2024-01-15T12:00:00Z" 12 } 13}

Important: The secret is only shown once. Store it securely for signature verification.

Update Webhook#

PATCH /webhooks/:id

Request Body#

FieldTypeDescription
urlstringEndpoint URL
eventsarrayEvents to subscribe to
statusstringactive or disabled
metadataobjectCustom metadata

Example Request#

1curl -X PATCH https://api.bootspring.dev/v1/webhooks/wh_abc123 \ 2 -H "Authorization: Bearer bs_xxx" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "events": ["project.created", "project.updated"], 6 "status": "active" 7 }'

Delete Webhook#

DELETE /webhooks/:id

Example Request#

curl -X DELETE https://api.bootspring.dev/v1/webhooks/wh_abc123 \ -H "Authorization: Bearer bs_xxx"

List Deliveries#

GET /webhooks/:id/deliveries

Query Parameters#

ParameterTypeDescription
statusstringFilter: success, failed, pending
limitintegerNumber of deliveries (default: 20)

Response#

1{ 2 "data": [ 3 { 4 "id": "del_xyz789", 5 "event": "project.created", 6 "status": "success", 7 "statusCode": 200, 8 "duration": 145, 9 "attemptCount": 1, 10 "request": { 11 "headers": { "Content-Type": "application/json" }, 12 "body": "{...}" 13 }, 14 "response": { 15 "statusCode": 200, 16 "body": "OK" 17 }, 18 "createdAt": "2024-01-15T10:30:00Z" 19 } 20 ] 21}

Send Test Event#

POST /webhooks/:id/test

Request Body#

FieldTypeRequiredDescription
eventstringYesEvent type to simulate

Example Request#

1curl -X POST https://api.bootspring.dev/v1/webhooks/wh_abc123/test \ 2 -H "Authorization: Bearer bs_xxx" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "event": "project.created" 6 }'

Webhook Payload Format#

Headers#

POST /your-webhook-endpoint Content-Type: application/json X-Bootspring-Signature: sha256=abc123... X-Bootspring-Delivery: del_xyz789 X-Bootspring-Event: project.created

Body#

1{ 2 "id": "evt_abc123", 3 "type": "project.created", 4 "timestamp": "2024-01-15T10:30:00Z", 5 "data": { 6 "project": { 7 "id": "proj_xyz", 8 "name": "New Project", 9 "createdAt": "2024-01-15T10:30:00Z" 10 } 11 } 12}

Signature Verification#

Verify webhook signatures to ensure requests are from Bootspring.

Node.js#

1import crypto from 'crypto'; 2 3function verifyWebhookSignature( 4 payload: string, 5 signature: string, 6 secret: string 7): boolean { 8 const expectedSignature = crypto 9 .createHmac('sha256', secret) 10 .update(payload) 11 .digest('hex'); 12 13 const expected = `sha256=${expectedSignature}`; 14 15 return crypto.timingSafeEqual( 16 Buffer.from(signature), 17 Buffer.from(expected) 18 ); 19} 20 21// Express middleware 22app.post('/webhooks/bootspring', (req, res) => { 23 const signature = req.headers['x-bootspring-signature']; 24 const payload = JSON.stringify(req.body); 25 26 if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) { 27 return res.status(401).send('Invalid signature'); 28 } 29 30 // Process webhook 31 const event = req.body; 32 console.log(`Received ${event.type}:`, event.data); 33 34 res.status(200).send('OK'); 35});

Python#

1import hmac 2import hashlib 3 4def verify_webhook_signature(payload: str, signature: str, secret: str) -> bool: 5 expected = 'sha256=' + hmac.new( 6 secret.encode(), 7 payload.encode(), 8 hashlib.sha256 9 ).hexdigest() 10 11 return hmac.compare_digest(signature, expected)

Retry Policy#

Failed deliveries are retried with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
612 hours
724 hours

After 7 failed attempts, the delivery is marked as failed.

Best Practices#

1. Respond Quickly#

Return a 2xx response within 30 seconds:

1app.post('/webhooks', async (req, res) => { 2 // Acknowledge immediately 3 res.status(200).send('OK'); 4 5 // Process asynchronously 6 processWebhook(req.body).catch(console.error); 7});

2. Handle Duplicates#

Webhooks may be delivered multiple times. Use the event id for idempotency:

1const processedEvents = new Set(); 2 3async function handleWebhook(event) { 4 if (processedEvents.has(event.id)) { 5 return; // Already processed 6 } 7 8 processedEvents.add(event.id); 9 // Process event... 10}

3. Verify Signatures#

Always verify signatures in production.

4. Use HTTPS#

Webhook endpoints must use HTTPS.

Webhook Limits#

PlanWebhooksEvents/Hour
Free1100
Pro51,000
Team2010,000
EnterpriseUnlimitedUnlimited

Errors#

CodeDescription
webhook_not_foundWebhook doesn't exist
webhook_limit_exceededMaximum webhooks reached
invalid_urlURL is not valid HTTPS
invalid_eventUnknown event type