chargebee-webhooks
Receive and verify Chargebee webhooks. Use when setting up Chargebee webhook handlers, debugging Basic Auth verification, or handling subscription billing events.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install hookdeck-webhook-skills-chargebee-webhooks
Repository
Skill path: skills/chargebee-webhooks
Receive and verify Chargebee webhooks. Use when setting up Chargebee webhook handlers, debugging Basic Auth verification, or handling subscription billing events.
Open repositoryBest for
Primary workflow: Run DevOps.
Technical facets: Full Stack, Security, Testing, Integration.
Target audience: everyone.
License: MIT.
Original source
Catalog source: SkillHub Club.
Repository owner: hookdeck.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install chargebee-webhooks into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/hookdeck/webhook-skills before adding chargebee-webhooks to shared team environments
- Use chargebee-webhooks for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: chargebee-webhooks
description: >
Receive and verify Chargebee webhooks. Use when setting up Chargebee webhook
handlers, debugging Basic Auth verification, or handling subscription billing events.
license: MIT
metadata:
author: hookdeck
version: "0.1.0"
repository: https://github.com/hookdeck/webhook-skills
---
# Chargebee Webhooks
## When to Use This Skill
- Setting up Chargebee webhook handlers
- Debugging Basic Auth verification failures
- Understanding Chargebee event types and payloads
- Processing subscription billing events
## Essential Code
Chargebee uses Basic Authentication for webhook verification. Here's how to implement it:
### Express.js
```javascript
// Verify Chargebee webhook with Basic Auth
// NOTE: Chargebee uses Basic Auth (not HMAC signatures), so raw body access
// is not required. Use express.json() for automatic JSON parsing:
app.post('/webhooks/chargebee', express.json(), (req, res) => {
// Extract Basic Auth credentials
const auth = req.headers.authorization;
if (!auth || !auth.startsWith('Basic ')) {
return res.status(401).send('Unauthorized');
}
// Decode and verify credentials
const encoded = auth.substring(6);
const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
const [username, password] = decoded.split(':');
const expectedUsername = process.env.CHARGEBEE_WEBHOOK_USERNAME;
const expectedPassword = process.env.CHARGEBEE_WEBHOOK_PASSWORD;
if (username !== expectedUsername || password !== expectedPassword) {
return res.status(401).send('Invalid credentials');
}
// Access the parsed JSON directly
const event = req.body;
console.log(`Received ${event.event_type} event:`, event.id);
// Handle specific event types
switch (event.event_type) {
case 'subscription_created':
case 'subscription_changed':
case 'subscription_cancelled':
// Process subscription events
break;
case 'payment_succeeded':
case 'payment_failed':
// Process payment events
break;
}
res.status(200).send('OK');
});
// Note: If you later need raw body access (e.g., for HMAC signature
// verification with other providers), use express.raw():
// app.post('/webhooks/other', express.raw({ type: 'application/json' }), (req, res) => {
// const rawBody = req.body.toString();
// // ... verify signature using rawBody ...
// });
```
### Next.js (App Router)
```typescript
// app/webhooks/chargebee/route.ts
import { NextRequest } from 'next/server';
export async function POST(req: NextRequest) {
// Extract Basic Auth credentials
const auth = req.headers.get('authorization');
if (!auth || !auth.startsWith('Basic ')) {
return new Response('Unauthorized', { status: 401 });
}
// Decode and verify credentials
const encoded = auth.substring(6);
const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
const [username, password] = decoded.split(':');
const expectedUsername = process.env.CHARGEBEE_WEBHOOK_USERNAME;
const expectedPassword = process.env.CHARGEBEE_WEBHOOK_PASSWORD;
if (username !== expectedUsername || password !== expectedPassword) {
return new Response('Invalid credentials', { status: 401 });
}
// Process the webhook
const event = await req.json();
console.log(`Received ${event.event_type} event:`, event.id);
return new Response('OK', { status: 200 });
}
```
### FastAPI
```python
# main.py
from fastapi import FastAPI, Header, HTTPException, Depends
from typing import Optional
import base64
import os
app = FastAPI()
def verify_chargebee_auth(authorization: Optional[str] = Header(None)):
"""Verify Chargebee webhook Basic Auth"""
if not authorization or not authorization.startswith("Basic "):
raise HTTPException(status_code=401, detail="Unauthorized")
# Decode credentials
encoded = authorization[6:]
decoded = base64.b64decode(encoded).decode('utf-8')
# Split username:password (handle colons in password)
if ':' not in decoded:
raise HTTPException(status_code=401, detail="Invalid authorization format")
colon_index = decoded.index(':')
username = decoded[:colon_index]
password = decoded[colon_index + 1:]
expected_username = os.getenv("CHARGEBEE_WEBHOOK_USERNAME")
expected_password = os.getenv("CHARGEBEE_WEBHOOK_PASSWORD")
if username != expected_username or password != expected_password:
raise HTTPException(status_code=401, detail="Invalid credentials")
return True
@app.post("/webhooks/chargebee")
async def handle_chargebee_webhook(
event: dict,
auth_valid: bool = Depends(verify_chargebee_auth)
):
"""Handle Chargebee webhook events"""
event_type = event.get("event_type")
print(f"Received {event_type} event: {event.get('id')}")
# Process event based on type
if event_type in ["subscription_created", "subscription_changed", "subscription_cancelled"]:
# Handle subscription events
pass
elif event_type in ["payment_succeeded", "payment_failed"]:
# Handle payment events
pass
return {"status": "OK"}
```
## Common Event Types
> **⚠️ WARNING: Verify Event Names!**
>
> The event type names below are examples and **MUST be verified** against the [Chargebee API documentation](https://apidocs.chargebee.com/docs/api/events#event_types) for your specific Chargebee configuration. Event names can vary significantly between API versions and configurations.
>
> **Special attention required for:**
> - Payment events (shown as `payment_succeeded` and `payment_failed` below)
> - Invoice events (shown as `invoice_generated` below)
> - Any custom events specific to your Chargebee setup
>
> **Always check your Chargebee Webhook settings for the exact event names your account uses.**
| Event | Triggered When | Common Use Cases |
|-------|----------------|------------------|
| `subscription_created` | New subscription is created | Provision access, send welcome email |
| `subscription_changed` | Subscription is modified | Update user permissions, sync changes |
| `subscription_cancelled` | Subscription is cancelled | Revoke access, trigger retention flow |
| `subscription_reactivated` | Cancelled subscription is reactivated | Restore access, send notification |
| `payment_succeeded` | Payment is successfully processed | Update payment status, send receipt |
| `payment_failed` | Payment attempt fails | Retry payment, notify customer |
| `invoice_generated` | Invoice is created | Send invoice to customer |
| `customer_created` | New customer is created | Create user account, sync data |
## Environment Variables
```bash
# Chargebee webhook Basic Auth credentials
CHARGEBEE_WEBHOOK_USERNAME=your_webhook_username
CHARGEBEE_WEBHOOK_PASSWORD=your_webhook_password
```
## Local Development
For local webhook testing, use Hookdeck CLI:
```bash
brew install hookdeck/hookdeck/hookdeck
hookdeck listen 3000 --path /webhooks/chargebee
```
No account required. Provides local tunnel + web UI for inspecting requests.
## Reference Materials
- [Overview](references/overview.md) - What Chargebee webhooks are, common event types
- [Setup](references/setup.md) - Configure webhooks in Chargebee dashboard
- [Verification](references/verification.md) - Basic Auth verification details and gotchas
## Examples
- [Express Example](examples/express/) - Complete Express.js implementation with tests
- [Next.js Example](examples/nextjs/) - Next.js App Router implementation with tests
- [FastAPI Example](examples/fastapi/) - Python FastAPI implementation with tests
## Recommended: webhook-handler-patterns
We recommend installing the [webhook-handler-patterns](https://github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns) skill alongside this one for handler sequence, idempotency, error handling, and retry logic. Key references (open on GitHub):
- [Handler sequence](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md) — Verify first, parse second, handle idempotently third
- [Idempotency](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md) — Prevent duplicate processing
- [Error handling](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md) — Return codes, logging, dead letter queues
- [Retry logic](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md) — Provider retry schedules, backoff patterns
## Related Skills
- [stripe-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks) - Stripe payment webhook handling
- [shopify-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks) - Shopify e-commerce webhook handling
- [github-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks) - GitHub repository webhook handling
- [resend-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/resend-webhooks) - Resend email webhook handling
- [clerk-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/clerk-webhooks) - Clerk auth webhook handling
- [elevenlabs-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/elevenlabs-webhooks) - ElevenLabs webhook handling
- [openai-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/openai-webhooks) - OpenAI webhook handling
- [paddle-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/paddle-webhooks) - Paddle billing webhook handling
- [webhook-handler-patterns](https://github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns) - Handler sequence, idempotency, error handling, retry logic
- [hookdeck-event-gateway](https://github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway) - Webhook infrastructure that replaces your queue — guaranteed delivery, automatic retries, replay, rate limiting, and observability for your webhook handlers
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/overview.md
```markdown
# Chargebee Webhooks Overview
## What Are Chargebee Webhooks?
Chargebee webhooks are HTTP callbacks that notify your application when events occur in your Chargebee account. They enable real-time updates about subscriptions, payments, customers, and other billing-related activities without polling the API.
## Common Event Types
| Event | Triggered When | Common Use Cases |
|-------|----------------|------------------|
| `subscription_created` | New subscription is created | Provision user access, create account, send welcome email |
| `subscription_changed` | Subscription's recurring items change | Update user permissions, sync subscription data |
| `subscription_cancelled` | Customer cancels subscription | Schedule access revocation, trigger retention campaigns |
| `subscription_reactivated` | Cancelled subscription is reactivated | Restore user access, update billing status |
| `subscription_renewed` | Subscription auto-renews | Log renewal, send confirmation |
| `payment_succeeded` | Payment is successfully processed | Update payment status, send receipt |
| `payment_failed` | Payment attempt fails | Send payment failure notification, retry logic |
| `invoice_generated` | New invoice is created | Send invoice to customer, update accounting |
| `customer_created` | New customer record created | Create user account, sync customer data |
| `customer_updated` | Customer information changes | Update user profile, sync changes |
## Event Payload Structure
All Chargebee webhook events follow a consistent structure:
```json
{
"id": "ev_16BHbhF4s42tO2lK",
"occurred_at": 1704067200,
"source": "admin_console",
"object": "event",
"api_version": "v2",
"event_type": "subscription_created",
"content": {
"subscription": {
"id": "16BHbhF4s42tO2lJ",
"customer_id": "16BHbhF4s42tO2lI",
"plan_id": "basic-monthly",
"status": "active",
"current_term_start": 1704067200,
"current_term_end": 1706745600,
"created_at": 1704067200
},
"customer": {
"id": "16BHbhF4s42tO2lI",
"email": "[email protected]",
"first_name": "John",
"last_name": "Doe"
}
}
}
```
### Key Fields
- `id`: Unique identifier for the webhook event
- `occurred_at`: Unix timestamp when the event occurred
- `event_type`: Type of event (e.g., subscription_created)
- `content`: Event-specific data containing affected resources
- `api_version`: API version used for the event format
## Webhook Delivery
### Timing and Order
- Webhooks are sent asynchronously after events occur
- Multiple webhooks may be sent simultaneously
- Events may arrive out of order
- Same event may be delivered multiple times (implement idempotency)
### Retry Mechanism
- Chargebee retries failed webhooks up to 7 times
- Retry intervals increase exponentially (2 minutes to 2 days)
- Webhook is marked failed if all retries are exhausted
### Response Requirements
- Your endpoint must return a 2XX status code
- Response body is ignored by Chargebee
- Timeouts: 20s connection, 20s read, 60s total execution
## Full Event Reference
For the complete list of events and their payloads, see [Chargebee's Event Types documentation](https://www.chargebee.com/docs/2.0/events_and_webhooks.html#event-types).
```
### references/setup.md
```markdown
# Setting Up Chargebee Webhooks
## Prerequisites
- Chargebee account with admin access
- Your application's webhook endpoint URL
- HTTPS endpoint (required for production)
## Configure Webhook Endpoint
1. Log in to your Chargebee dashboard
2. Navigate to **Settings** → **Webhooks**
- Note: The exact path may vary. Look for "Webhooks" under Settings, Configure Chargebee, or Developer Settings
3. Click **Add Webhook** or **New Webhook** button
## Webhook Configuration
### Basic Settings
1. **Webhook Name**: Give your webhook a descriptive name (e.g., "Production App Webhook")
2. **Webhook URL**: Enter your endpoint URL (e.g., `https://app.example.com/webhooks/chargebee`)
### Enable Basic Authentication (Recommended)
1. Toggle **"Protect webhook URL with basic authentication"** to ON
2. Set **Username**: Choose a username for Basic Auth
3. Set **Password**: Choose a strong password for Basic Auth
4. Save these credentials - you'll need them in your application:
```bash
CHARGEBEE_WEBHOOK_USERNAME=your_chosen_username
CHARGEBEE_WEBHOOK_PASSWORD=your_chosen_password
```
### Alternative: Custom Key in URL
If you can't use Basic Auth, include a secret key in your webhook URL:
```
https://app.example.com/webhooks/chargebee?key=your_secret_key_here
```
### Select Events
1. Choose which events to receive:
- **All Events**: Receive notifications for all event types
- **Selected Events**: Choose specific events (recommended)
2. Common events to select:
- Subscription Events: `subscription_created`, `subscription_changed`, `subscription_cancelled`
- Payment Events: `payment_succeeded`, `payment_failed`
- Invoice Events: `invoice_generated`, `invoice_updated`
- Customer Events: `customer_created`, `customer_updated`
3. Click **Create Webhook** to save
## Test Your Webhook
### Using Chargebee Test Events
1. After creating your webhook, click on it in the webhooks list
2. Click **Test Webhook** button
3. Select an event type to send
4. Click **Send Test Event**
5. Check your application logs to verify receipt
### Manual Testing
You can also trigger real events in test mode:
1. Ensure you're in Chargebee test mode
2. Create a test subscription or process a test payment
3. Monitor your webhook endpoint for incoming events
## Production Considerations
### Security
- Always use HTTPS endpoints
- Implement Basic Auth verification
- Store credentials securely (environment variables)
- Never log full webhook payloads in production
### Reliability
- Respond quickly (within 20 seconds)
- Process events asynchronously if needed
- Implement idempotency to handle duplicate events
- Return 2XX status codes for successful processing
### Monitoring
- Set up alerts for webhook failures
- Monitor webhook processing time
- Track event types and volumes
- Review failed webhooks in Chargebee dashboard
## Multiple Environments
Create separate webhooks for each environment:
- Development: Local tunnel URL (via Hookdeck CLI)
- Staging: Staging server URL with test credentials
- Production: Production URL with production credentials
Chargebee allows up to 5 webhook endpoints, making it easy to manage multiple environments.
```
### references/verification.md
```markdown
# Chargebee Webhook Verification
## How It Works
Chargebee uses HTTP Basic Authentication to verify webhooks. When Chargebee sends a webhook to your endpoint, it includes an `Authorization` header with Base64-encoded credentials.
### Basic Auth Format
```
Authorization: Basic base64(username:password)
```
For example, if your credentials are:
- Username: `webhook_user`
- Password: `secret_pass`
The header would be:
```
Authorization: Basic d2ViaG9va191c2VyOnNlY3JldF9wYXNz
```
## Implementation
### Manual Verification (Recommended)
Here's how to manually verify Basic Auth in different languages:
#### Node.js/Express
```javascript
function verifyChargebeeAuth(req, res, next) {
const auth = req.headers.authorization;
if (!auth || !auth.startsWith('Basic ')) {
return res.status(401).send('Unauthorized');
}
// Decode Base64
const encoded = auth.substring(6);
const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
// Split username:password
const colonIndex = decoded.indexOf(':');
if (colonIndex === -1) {
return res.status(401).send('Invalid authorization format');
}
const username = decoded.substring(0, colonIndex);
const password = decoded.substring(colonIndex + 1);
// Verify credentials
const expectedUsername = process.env.CHARGEBEE_WEBHOOK_USERNAME;
const expectedPassword = process.env.CHARGEBEE_WEBHOOK_PASSWORD;
if (username !== expectedUsername || password !== expectedPassword) {
return res.status(401).send('Invalid credentials');
}
next();
}
app.post('/webhooks/chargebee', express.json(), verifyChargebeeAuth, (req, res) => {
// Handle webhook
});
```
#### Python/FastAPI
```python
import base64
from fastapi import Header, HTTPException
def verify_chargebee_auth(authorization: str = Header(None)):
if not authorization or not authorization.startswith("Basic "):
raise HTTPException(status_code=401, detail="Unauthorized")
# Decode Base64
encoded = authorization[6:]
try:
decoded = base64.b64decode(encoded).decode('utf-8')
except Exception:
raise HTTPException(status_code=401, detail="Invalid authorization encoding")
# Split username:password
if ':' not in decoded:
raise HTTPException(status_code=401, detail="Invalid authorization format")
username, password = decoded.split(':', 1)
# Verify credentials
expected_username = os.getenv("CHARGEBEE_WEBHOOK_USERNAME")
expected_password = os.getenv("CHARGEBEE_WEBHOOK_PASSWORD")
if username != expected_username or password != expected_password:
raise HTTPException(status_code=401, detail="Invalid credentials")
return True
```
## Common Gotchas
### 1. Missing Authorization Header
Some proxies or load balancers may strip the Authorization header. Ensure your infrastructure preserves headers.
### 2. Case-Sensitive Headers
Header names are case-insensitive, but some frameworks normalize them:
- Express: `req.headers.authorization` (lowercase)
- Some frameworks: `req.headers['Authorization']` (original case)
### 3. Colon in Password
Passwords can contain colons. Always split on the first colon only:
```javascript
// CORRECT - splits on first colon only
const colonIndex = decoded.indexOf(':');
const username = decoded.substring(0, colonIndex);
const password = decoded.substring(colonIndex + 1);
// WRONG - splits on all colons
const [username, password] = decoded.split(':'); // Breaks if password has ':'
```
### 4. Base64 Padding
Ensure proper Base64 decoding that handles padding correctly. Most standard libraries handle this automatically.
### 5. Empty Credentials
Handle edge cases where username or password might be empty:
```javascript
if (!username || !password) {
return res.status(401).send('Empty credentials');
}
```
## Debugging Verification Failures
### 1. Log the Authorization Header (Development Only)
```javascript
console.log('Auth header:', req.headers.authorization);
console.log('Decoded:', Buffer.from(auth.substring(6), 'base64').toString());
```
**WARNING**: Never log credentials in production!
### 2. Common Error Messages
| Error | Possible Cause | Solution |
|-------|----------------|----------|
| "Missing authorization header" | No header sent | Check Chargebee webhook config |
| "Invalid authorization format" | Malformed header | Verify Basic Auth is enabled |
| "Invalid credentials" | Wrong username/password | Check environment variables |
| "Invalid base64" | Encoding issue | Check for header corruption |
### 3. Test with curl
Test your endpoint with curl to isolate issues:
```bash
# Calculate Base64 for your credentials
echo -n "your_username:your_password" | base64
# Test the endpoint
curl -X POST https://your-app.com/webhooks/chargebee \
-H "Authorization: Basic eW91cl91c2VybmFtZTp5b3VyX3Bhc3N3b3Jk" \
-H "Content-Type: application/json" \
-d '{"event_type": "test", "id": "test_event"}'
```
## Security Best Practices
1. **Use Environment Variables**: Never hardcode credentials
2. **Use HTTPS**: Always use HTTPS in production
3. **Timing-Safe Comparison**: While not critical for Basic Auth, consider using timing-safe string comparison
4. **Rate Limiting**: Implement rate limiting to prevent brute force attacks
5. **Fail Fast**: Return 401 immediately on auth failure
6. **Don't Reveal Details**: Use generic error messages like "Unauthorized"
```