Documentation Index
Fetch the complete documentation index at: https://mintlify.com/useautumn/autumn/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Autumn sends webhooks to notify your application of important events like:
- Subscription changes (new, upgrade, downgrade, cancel)
- Usage threshold alerts
- Payment events
- Balance updates
Webhooks are powered by Svix and include built-in signature verification, automatic retries, and a testing playground.
Setup
1. Create a Webhook Endpoint
Create an endpoint in your application to receive webhooks:
// app/api/webhooks/autumn/route.ts
import { headers } from 'next/headers';
import { Webhook } from 'svix';
export async function POST(req: Request) {
const payload = await req.text();
const headersList = headers();
const svixId = headersList.get('svix-id');
const svixTimestamp = headersList.get('svix-timestamp');
const svixSignature = headersList.get('svix-signature');
// Verify webhook signature
const wh = new Webhook(process.env.AUTUMN_WEBHOOK_SECRET!);
let event;
try {
event = wh.verify(payload, {
'svix-id': svixId!,
'svix-timestamp': svixTimestamp!,
'svix-signature': svixSignature!,
});
} catch (err) {
console.error('Webhook verification failed:', err);
return new Response('Invalid signature', { status: 400 });
}
// Handle the event
switch (event.type) {
case 'customer.products.updated':
await handleProductUpdate(event.data);
break;
case 'customer.threshold_reached':
await handleThresholdReached(event.data);
break;
}
return new Response('Webhook processed', { status: 200 });
}
- Go to the Autumn dashboard → Settings → Webhooks
- Click Add Endpoint
- Enter your webhook URL (e.g.,
https://api.yourapp.com/webhooks/autumn)
- Select which events to receive
- Save and copy the Signing Secret to your environment variables
For local development, use tools like ngrok or Svix Play to expose your local server.
Event Types
customer.products.updated
Sent when a customer’s product subscription changes.
Scenarios:
new - Customer subscribed to a new product
upgrade - Customer upgraded to a higher tier
downgrade - Customer downgraded to a lower tier
cancel - Customer canceled their subscription
Payload:
{
type: "customer.products.updated",
data: {
scenario: "new" | "upgrade" | "downgrade" | "cancel",
customer: {
id: string,
email: string,
name: string,
// ... full customer object
},
updated_product: {
id: string,
name: string,
status: "active" | "scheduled" | "canceling",
// ... full product object
},
entity?: {
id: string,
name: string
}
}
}
Example Handler:
async function handleProductUpdate(data: CustomerProductsUpdatedData) {
const { scenario, customer, updated_product } = data;
switch (scenario) {
case 'new':
// Customer subscribed to new product
await sendWelcomeEmail(customer.email, updated_product.name);
await syncToDatabase({ customerId: customer.id, product: updated_product });
break;
case 'upgrade':
// Customer upgraded
await sendUpgradeEmail(customer.email, updated_product.name);
await unlockPremiumFeatures(customer.id);
break;
case 'downgrade':
// Customer downgraded
await sendDowngradeEmail(customer.email, updated_product.name);
await restrictFeatures(customer.id, updated_product);
break;
case 'cancel':
// Customer canceled
await sendCancellationEmail(customer.email);
await scheduleDataRetention(customer.id);
break;
}
}
customer.threshold_reached
Sent when a customer reaches a usage threshold (e.g., 80% of their limit).
Payload:
{
type: "customer.threshold_reached",
data: {
customer: {
id: string,
email: string,
name: string
},
feature: {
id: string,
name: string
},
threshold: number, // Threshold percentage (e.g., 80)
usage: number, // Current usage
limit: number, // Total limit
remaining: number // Amount remaining
}
}
Example Handler:
async function handleThresholdReached(data: ThresholdReachedData) {
const { customer, feature, threshold, usage, limit, remaining } = data;
// Send warning email
await sendEmail({
to: customer.email,
subject: `${feature.name} usage at ${threshold}%`,
body: `
You've used ${usage} of ${limit} ${feature.name}.
${remaining} remaining.
Upgrade now to get more: https://yourapp.com/upgrade
`
});
// Send in-app notification
await createNotification({
customerId: customer.id,
title: `${feature.name} limit approaching`,
message: `${remaining} ${feature.name} remaining`,
action: { label: "Upgrade", url: "/upgrade" }
});
}
Security
Verify Webhook Signatures
Always verify webhook signatures to ensure requests are from Autumn:
import { Webhook } from 'svix';
const wh = new Webhook(process.env.AUTUMN_WEBHOOK_SECRET!);
try {
const event = wh.verify(payload, headers);
// Event is verified, safe to process
} catch (err) {
// Invalid signature - reject the request
throw new Error('Invalid webhook signature');
}
Use HTTPS
Webhook endpoints must use HTTPS in production. Svix will reject HTTP endpoints.
Implement Idempotency
Webhooks may be delivered more than once. Use the svix-id header as an idempotency key:
const eventId = headers['svix-id'];
// Check if already processed
if (await hasProcessedEvent(eventId)) {
return new Response('Already processed', { status: 200 });
}
// Process event
await handleEvent(event);
// Mark as processed
await markEventProcessed(eventId);
Testing
Local Testing with Svix Play
- Go to Svix Play
- Generate a webhook URL
- Add it as a webhook endpoint in Autumn dashboard
- Trigger events (attach, track, etc.) and see them arrive in real-time
Send Test Events
Use the Autumn dashboard to send test webhook events:
- Go to Settings → Webhooks
- Click on your endpoint
- Click Send Example and select an event type
CLI Testing
# Install Svix CLI
npm install -g svix-cli
# Listen for webhooks locally
svix listen http://localhost:3000/webhooks/autumn
Retries and Reliability
Autumn automatically retries failed webhook deliveries:
- Retry Schedule: Exponential backoff (1min, 5min, 30min, 2hr, 5hr, 10hr, 10hr)
- Success Criteria: HTTP 200-299 response
- Timeout: 15 seconds per attempt
- Total Attempts: Up to 7 retries over ~24 hours
Return Proper Status Codes
// ✅ Success - webhook processed
return new Response('OK', { status: 200 });
// ✅ Will retry - temporary error
return new Response('Database unavailable', { status: 503 });
// ❌ Won't retry - permanent error
return new Response('Invalid event type', { status: 400 });
Debugging
View Webhook Logs
- Go to Settings → Webhooks in Autumn dashboard
- Click on your endpoint
- View delivery attempts, status codes, and response bodies
Common Issues
Signature verification fails
- Check that you’re using the correct webhook secret
- Ensure you’re passing the raw request body (not parsed JSON)
- Verify all three Svix headers are present:
svix-id, svix-timestamp, svix-signature
- Verify your endpoint is publicly accessible (use ngrok for local dev)
- Check that the endpoint uses HTTPS (required in production)
- Ensure you’ve selected the correct events in webhook configuration
- Check webhook logs in dashboard for delivery failures
Duplicate webhook deliveries
- This is normal behavior - implement idempotency using
svix-id header
- Check your handler is returning 200 on success
Best Practices
Process webhooks asynchronously
Return 200 immediately and process events in a background job:app.post('/webhooks/autumn', async (req, res) => {
const event = await verifyWebhook(req);
// Queue for processing
await queue.add('process-webhook', event);
// Return immediately
res.status(200).send('Queued');
});
Implement idempotency
Use svix-id to prevent duplicate processing:const eventId = headers['svix-id'];
if (await cache.get(eventId)) {
return res.status(200).send('Already processed');
}
await processEvent(event);
await cache.set(eventId, true, 86400); // 24hr TTL
Monitor webhook health
Track webhook failures and alert on repeated errors:if (failureCount > 3) {
await alertOncall('Webhook failures detected');
}
Version your webhook handlers
Plan for schema changes by versioning handlers:const handlers = {
'customer.products.updated': {
v1: handleProductUpdateV1,
v2: handleProductUpdateV2,
}
};
Next Steps
API Reference
Explore all available endpoints
SDK Documentation
Learn how to use Autumn SDKs