Webhooks
Webhooks allow your application to receive real-time notifications when events occur in the Sweet Spot API. Instead of polling for changes, webhooks push event data to your endpoint as events happen.
Overview
When you register a webhook, you provide an HTTPS endpoint URL and select which events you want to receive. When those events occur, we send an HTTP POST request to your endpoint with the event data.
Getting Started
- Create a Webhook: Register a webhook endpoint and select which events to subscribe to
- Store Your Secret: Save the webhook secret securely—you'll need it to verify incoming requests
- Receive Events: When subscribed events occur, we'll POST event data to your endpoint
- Verify & Process: Always verify the webhook signature before processing events
Tip: Use the test webhook endpoint to send test events to your endpoint and verify your integration before going live.
Available Events
Subscribe to specific event types to receive notifications. Events are organized by resource type. There are 58 event types available across all categories.
Checkout Lifecycle Events
Track a checkout from creation through fulfillment and delivery.
| Event Type | Description | When Triggered |
|---|---|---|
checkout.created | New checkout was created | Customer initiates checkout flow |
checkout.updated | Checkout was modified | Any checkout field update |
checkout.completed | Lease is now active | Fulfillment complete, lease active (LeaseActive state) |
checkout.cancelled | Checkout was cancelled | Admin or customer cancellation |
checkout.expired | Checkout timed out | Checkout exceeded expiration window |
checkout.deposit_paid | Deposit payment confirmed | Deposit payment succeeds |
checkout.documents_signed | Lease documents signed | Document signing completed |
checkout.order_confirmed | Ready to ship product | Documents signed and deposit captured (FulfillmentPending state) |
checkout.fulfilled | Order was fulfilled | Fulfillment completed |
checkout.shipped | Tracking number added | Shipment tracking attached |
checkout.delivered | Package delivered | Carrier confirms delivery |
checkout.tracking_updated | Tracking status changed | Any tracking status update |
checkout.fulfillment_exception | Delivery issue occurred | Return or delivery failure |
checkout.tracking_stale | Tracking stalled | No updates for extended period |
checkout.deposit_reset | Deposit reset by admin | Admin resets failed ACH deposit |
Key Events for Fulfillment
- checkout.order_confirmed — This is your signal to start fulfillment. The customer has signed documents and the deposit has been captured. Ship the product now.
- checkout.completed — The entire checkout process is done. Fulfillment is complete, the customer has the product, and the lease is active.
Subscription & Recurring Payment Events
Monitor subscription status and recurring payment outcomes.
| Event Type | Description | When Triggered |
|---|---|---|
checkout.subscription_active | Subscription activated | Recurring billing goes active |
checkout.subscription_past_due | Payment past due | Subscription payment fails |
checkout.subscription_cancelled | Subscription cancelled | Subscription terminated |
checkout.payment_failed | Recurring payment failed | Invoice payment fails |
checkout.payment_recovered | Payment recovered | Past due payment succeeds |
ACH & Deposit Payment Events
Track ACH bank transfer status for deposit payments.
| Event Type | Description | When Triggered |
|---|---|---|
checkout.payment_processing | ACH payment in-flight | Bank transfer initiated |
checkout.deposit_failed | ACH payment failed | Bank transfer rejected/returned |
checkout.payment_action_required | Action needed | Customer action required |
checkout.payment_expired | Pre-auth expired | Payment pre-authorization expired |
Customer Events
| Event Type | Description | When Triggered |
|---|---|---|
customer.created | New customer created | Customer record created |
customer.updated | Customer updated | Customer info modified |
customer.credit_check_completed | Credit check done | Credit verification finished |
Product Events
| Event Type | Description | When Triggered |
|---|---|---|
product.created | Product created | New product added |
product.updated | Product updated | Product info modified |
product.deleted | Product deleted | Product removed |
Lease & Lease Term Events
| Event Type | Description | When Triggered |
|---|---|---|
lease.started | Lease began | Lease term started |
lease.ended | Lease ended | Lease term completed |
lease_term.created | Lease term created | New term added |
lease_term.deleted | Lease term deleted | Term removed |
Product Fee Events
| Event Type | Description | When Triggered |
|---|---|---|
product_fee.created | Fee created | New fee added |
product_fee.updated | Fee updated | Fee modified |
product_fee.deleted | Fee deleted | Fee removed |
Payment Events
| Event Type | Description | When Triggered |
|---|---|---|
payment.received | Payment received | Any payment recorded |
Fulfillment Events
| Event Type | Description | When Triggered |
|---|---|---|
fulfillment.created | Fulfillment created | Fulfillment record created |
fulfillment.updated | Fulfillment updated | Fulfillment info modified |
Organization Events
| Event Type | Description | When Triggered |
|---|---|---|
organization.created | Organization created | New org added |
organization.updated | Organization updated | Org info modified |
User & API Token Events
| Event Type | Description | When Triggered |
|---|---|---|
user.created | User created | New user added |
user.updated | User updated | User info modified |
user.deleted | User deleted | User removed |
api_token.created | API token created | New API token generated |
api_token.revoked | API token revoked | API token invalidated |
Webhook Management Events
| Event Type | Description | When Triggered |
|---|---|---|
webhook.created | Webhook registered | New webhook endpoint added |
webhook.updated | Webhook updated | Webhook config modified |
webhook.deleted | Webhook deleted | Webhook endpoint removed |
Invitation Events
| Event Type | Description | When Triggered |
|---|---|---|
invitation.created | Invitation sent | User invited to org |
invitation.accepted | Invitation accepted | User accepted invite |
Webhook Payload
When an event occurs, your endpoint receives an HTTP POST request with a JSON payload containing the event type, unique event ID, timestamp, and the relevant resource data.
Request Headers
Every webhook request includes these headers:
| Header | Description |
|---|---|
X-Webhook-Signature | HMAC-SHA256 signature (format: sha256=hex_signature) |
X-Webhook-Timestamp | Unix timestamp when the request was signed |
X-Webhook-Event-Type | The event type (e.g., checkout.created) |
X-Webhook-Event-ID | Unique identifier for this event (use for deduplication) |
Example Payload
{
"id": "evt_1234567890",
"type": "checkout.order_confirmed",
"data": {
"id": "chk_123456",
"state": "LeaseActive",
"productVariantId": "pvar_123456",
"customerId": "cust_123456",
"createdAt": "2024-01-15T10:30:00Z"
},
"createdAt": "2024-01-15T10:30:00Z"
}Delivery & Retries
We make every effort to deliver webhooks reliably. If your endpoint doesn't respond with a 2xx status code, we'll automatically retry with exponential backoff.
Retry Schedule
| Attempt | Delay |
|---|---|
| 1st attempt | Immediate |
| 2nd attempt | 60 seconds |
| 3rd attempt | 5 minutes |
| 4th attempt | 30 minutes |
| 5th attempt | 2 hours |
| Final attempt | 24 hours |
Retryable Errors
- 5xx server errors
- 429 rate limit errors
- Connection timeouts
Non-Retryable Errors
- 4xx client errors (except 429)
- Invalid endpoint URL
- SSL/TLS errors
Replaying Events: If you need to replay a failed webhook event, you can use the webhook audit endpoints to retrieve delivery logs and replay specific events.
Security
All webhook requests are signed so you can verify they came from Sweet Spot and haven't been tampered with.
Security Features
- HTTPS Required: Webhook URLs must use HTTPS (localhost and private IPs are blocked)
- HMAC Signing: Every request is signed with SHA-256 using your webhook secret
- Timestamp Validation: Timestamps allow you to reject stale requests and prevent replay attacks
- Full Audit Trail: All webhook deliveries are logged for debugging and compliance
Signature Format
The signature is computed as:
signature = hex(HMAC-SHA256(secret, timestamp + "." + payload))To verify a webhook:
- Extract the signature from
X-Webhook-Signature(removesha256=prefix) - Get the timestamp from
X-Webhook-Timestamp - Reconstruct the signed payload:
timestamp + "." + request_body - Compute HMAC-SHA256 with your webhook secret
- Compare using constant-time comparison
- Optionally reject requests older than 5 minutes
Example: Verify Signature (Node.js)
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret, timestamp) {
// Reconstruct the signed payload
const signedPayload = `${timestamp}.${payload}`;
// Compute the expected signature
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Extract the signature from the header (format: sha256=hex_signature)
const receivedSignature = signature.replace('sha256=', '');
// Compare signatures using constant-time comparison
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(receivedSignature)
);
}
// Example usage in Express.js
app.post('/webhooks/sweetspot', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const eventType = req.headers['x-webhook-event-type'];
const eventId = req.headers['x-webhook-event-id'];
const webhookSecret = process.env.WEBHOOK_SECRET;
if (!verifyWebhookSignature(req.body, signature, webhookSecret, timestamp)) {
return res.status(401).send('Invalid signature');
}
// Check timestamp to prevent replay attacks (recommended)
const currentTime = Math.floor(Date.now() / 1000);
const requestTime = parseInt(timestamp);
if (Math.abs(currentTime - requestTime) > 300) { // 5 minutes tolerance
return res.status(401).send('Request timestamp too old');
}
const event = JSON.parse(req.body);
// Process the webhook event
console.log('Received event:', eventType, eventId);
res.status(200).send('OK');
});Important: Always verify webhook signatures before processing events. Never trust webhook requests without verification. Store your webhook secret securely and never expose it in client-side code.
Best Practices
1. Always Verify Signatures
Never process webhook events without verifying the HMAC signature. This ensures the request is authentic and hasn't been tampered with.
2. Respond Quickly (Within 5 Seconds)
Return a 2xx status code as quickly as possible. If you need to do heavy processing, acknowledge the webhook first, then process asynchronously using a job queue.
3. Handle Duplicates Idempotently
Webhooks may be delivered multiple times during retries. Use the X-Webhook-Event-IDheader to deduplicate and ensure your processing is idempotent.
4. Use HTTPS Endpoints
Webhook URLs must use HTTPS to ensure payload encryption in transit. HTTP endpoints and localhost/private IPs are not supported.
5. Test Before Going Live
Use the test webhook endpoint to send test events and verify your implementation handles events correctly before subscribing to production events.
6. Monitor Delivery Health
Use the webhook audit endpoints to monitor delivery success rates and investigate failures. Set up alerts for repeated delivery failures.