Webhooks
Webhooks notify your application in real time when events happen in SubscribeFlow. Instead of polling the API, you register an endpoint URL and SubscribeFlow sends an HTTP POST request for every matching event.
Create a webhook endpoint
Warning
The signing secret is only returned when the webhook is created. Store it in a secret manager or environment variable immediately.
Event types
| Event | Description |
|---|---|
subscriber.created |
A new subscriber was added |
subscriber.updated |
Subscriber data (email, status, metadata) changed |
subscriber.deleted |
A subscriber was permanently deleted |
tag.subscribed |
A subscriber was added to a tag |
tag.unsubscribed |
A subscriber was removed from a tag |
subscriber.note_added |
A note was added to a subscriber |
Each webhook delivery includes a JSON payload with the event type, a timestamp, and the full resource object.
Signature verification
Every delivery includes an X-SubscribeFlow-Signature header containing an HMAC-SHA256 signature of the request body. Always verify this signature before processing the payload.
import hmac
import hashlib
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
# In your webhook handler (FastAPI example):
from fastapi import Request, HTTPException
@app.post("/webhooks/subscribeflow")
async def handle_webhook(request: Request):
body = await request.body()
signature = request.headers.get("X-SubscribeFlow-Signature", "")
if not verify_signature(body, signature, WEBHOOK_SECRET):
raise HTTPException(status_code=401, detail="Invalid signature")
event = await request.json()
print(f"Received: {event['event_type']}")
import crypto from 'node:crypto';
function verifySignature(
payload: string,
signature: string,
secret: string,
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return signature === `sha256=${expected}`;
}
// In your webhook handler (Express example):
import express from 'express';
const app = express();
app.use(express.raw({ type: 'application/json' }));
app.post('/webhooks/subscribeflow', (req, res) => {
const signature = req.headers['x-subscribeflow-signature'] as string;
const body = req.body.toString();
if (!verifySignature(body, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(body);
console.log('Received:', event.event_type);
res.sendStatus(200);
});
Warning
Always use a constant-time comparison function (like hmac.compare_digest in Python or crypto.timingSafeEqual in Node.js) to prevent timing attacks.
Delivery monitoring
List deliveries
Retry a failed delivery
Test a webhook
Send a test event to verify your endpoint is reachable and correctly configured.
Hands-On: React to subscriber events
Set up a webhook that logs every new subscriber to your application.
import asyncio
from subscribeflow import SubscribeFlowClient
async def main():
async with SubscribeFlowClient(api_key="sf_live_...") as client:
# 1. Create a webhook for new subscribers
webhook = await client.webhooks.create(
url="https://your-app.com/webhooks/subscribeflow",
events=[
"subscriber.created",
"subscriber.deleted",
"tag.subscribed",
"tag.unsubscribed",
],
description="Track all subscriber activity",
)
print(f"Webhook created: {webhook.id}")
print(f"Secret: {webhook.secret}")
# 2. Test the endpoint
result = await client.webhooks.test(webhook.id)
if result.success:
print("Endpoint verified!")
else:
print(f"Test failed: {result.error}")
asyncio.run(main())
import { SubscribeFlowClient } from '@subscribeflow/sdk';
const client = new SubscribeFlowClient({
apiKey: 'sf_live_...',
});
// 1. Create a webhook for new subscribers
const webhook = await client.webhooks.create({
url: 'https://your-app.com/webhooks/subscribeflow',
events: [
'subscriber.created',
'subscriber.deleted',
'tag.subscribed',
'tag.unsubscribed',
],
description: 'Track all subscriber activity',
});
console.log('Webhook created:', webhook.id);
console.log('Secret:', webhook.signing_secret);
// 2. Test the endpoint
const result = await client.webhooks.test(webhook.id, 'subscriber.created');
if (result.success) {
console.log('Endpoint verified!');
} else {
console.log('Test failed:', result.error);
}
# 1. Create a webhook (save the secret from the response!)
curl -X POST https://api.subscribeflow.net/api/v1/webhooks \
-H "X-API-Key: sf_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/subscribeflow",
"events": ["subscriber.created", "subscriber.deleted", "tag.subscribed", "tag.unsubscribed"],
"description": "Track all subscriber activity"
}'
# 2. Test the endpoint
curl -X POST https://api.subscribeflow.net/api/v1/webhooks/WEBHOOK_ID/test \
-H "X-API-Key: sf_live_..."