Skip to main content

Webhooks

Required Role

Webhooks are available to Reseller and Agency accounts only.

Webhooks allow you to receive real-time HTTP notifications when license events occur. Instead of polling the API, your server is notified instantly.

Supported Events​

EventTriggered When
license.createdA new license is created
license.activatedA license is activated on a domain
license.deactivatedA license is deactivated from a domain
license.revokedA license is revoked
license.expiredA license expires (e.g., subscription expiration)

Setting Up a Webhook​

Via Portal​

  1. Go to Webhooks in your portal
  2. Click Create Webhook
  3. Enter:
    • URL — your HTTPS endpoint that will receive the webhook
    • Events — select which events to subscribe to
    • Description (optional) — for your own reference
  4. Click Create

A unique HMAC secret is automatically generated for signature verification.

Via API​

curl -X POST https://admin.flavorteam.dev/api/v1/portal/webhooks \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/flavor",
"events": ["license.created", "license.activated"],
"description": "Production webhook"
}'

Response:

{
"id": 1,
"url": "https://your-server.com/webhooks/flavor",
"events": ["license.created", "license.activated"],
"secret": "a1b2c3d4...64 hex chars...",
"is_active": true,
"description": "Production webhook",
"created_at": "2026-03-30T10:00:00Z"
}
caution

The secret is shown at creation time. Store it securely — you'll need it to verify webhook signatures.

Webhook Payload​

When an event occurs, Flavor Hub sends an HTTP POST to your URL:

POST /webhooks/flavor HTTP/1.1
Host: your-server.com
Content-Type: application/json
User-Agent: FlavorHub-Webhook/1.0
X-Hub-Event: license.created
X-Hub-Signature-256: sha256=abc123...
{
"event": "license.created",
"timestamp": "2026-03-30T12:00:00+00:00",
"data": {
"license_id": 42,
"license_key": "FLVR-AB12...",
"tier": "starter",
"product_slug": "flavor-starter",
"expires_at": "2027-03-30T12:00:00+00:00"
}
}

Event Data Fields​

license.created

{
"license_id": 42,
"license_key": "FLVR-AB12...",
"tier": "starter",
"product_slug": "flavor-starter",
"expires_at": "2027-03-30T12:00:00+00:00"
}

license.activated

{
"license_key": "FLVR-AB12...",
"domain": "client-site.com",
"tier": "starter",
"product_slug": "flavor-starter"
}

license.deactivated

{
"license_key": "FLVR-AB12...",
"domain": "client-site.com"
}

license.revoked

{
"license_key": "FLVR-AB12...",
"deactivated_domains": ["site1.com", "site2.com"]
}

license.expired

{
"customer_id": 15,
"licenses_revoked": 3,
"subscription_id": 8
}

Verifying Signatures​

Every webhook request includes an X-Hub-Signature-256 header containing an HMAC-SHA256 signature of the request body, signed with your webhook's secret.

Always verify this signature to ensure the request came from Flavor Hub.

Verification Example (Node.js)​

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}

// In your Express handler:
app.post('/webhooks/flavor', (req, res) => {
const signature = req.headers['x-hub-signature-256'];
const isValid = verifyWebhook(
JSON.stringify(req.body),
signature,
process.env.WEBHOOK_SECRET
);

if (!isValid) {
return res.status(401).send('Invalid signature');
}

// Process the webhook...
console.log('Event:', req.body.event);
console.log('Data:', req.body.data);

res.status(200).send('OK');
});

Verification Example (PHP)​

function verifyWebhook(string $payload, string $signature, string $secret): bool {
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
return hash_equals($expected, $signature);
}

// In your webhook handler:
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';

if (!verifyWebhook($payload, $signature, $webhookSecret)) {
http_response_code(401);
exit('Invalid signature');
}

$event = json_decode($payload, true);
// Process $event['event'] and $event['data']...

http_response_code(200);
echo 'OK';

Verification Example (Python)​

import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)

# In your Flask/FastAPI handler:
@app.post("/webhooks/flavor")
async def handle_webhook(request: Request):
payload = await request.body()
signature = request.headers.get("X-Hub-Signature-256", "")

if not verify_webhook(payload, signature, WEBHOOK_SECRET):
raise HTTPException(status_code=401, detail="Invalid signature")

event = await request.json()
# Process event["event"] and event["data"]...

return {"status": "ok"}

Testing Webhooks​

Use the Test button on any webhook to send a test ping:

curl -X POST https://admin.flavorteam.dev/api/v1/portal/webhooks/{id}/test \
-H "Authorization: Bearer eyJ..."

The test sends a ping to your URL and reports:

  • Success — your endpoint responded with 2xx
  • Status code — the HTTP response code
  • Error — any error message if the delivery failed

Delivery Logs​

View recent deliveries for any webhook:

curl "https://admin.flavorteam.dev/api/v1/portal/webhooks/{id}/deliveries?limit=25" \
-H "Authorization: Bearer eyJ..."

Each delivery log shows:

  • Event type — which event triggered the delivery
  • Payload — the full JSON sent
  • Status code — HTTP response from your server
  • Success — whether the delivery was successful (2xx)
  • Error — error message if failed
  • Timestamp

Up to 100 recent deliveries are stored per webhook.

Managing Webhooks​

Update​

curl -X PATCH https://admin.flavorteam.dev/api/v1/portal/webhooks/{id} \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://new-url.com/webhook",
"events": ["license.created"],
"is_active": false
}'

Delete​

curl -X DELETE https://admin.flavorteam.dev/api/v1/portal/webhooks/{id} \
-H "Authorization: Bearer eyJ..."

Deleting a webhook also removes all its delivery logs.

Limits​

  • Maximum 10 webhooks per account
  • Deliveries have a 10-second timeout — ensure your endpoint responds quickly
  • Return a 2xx status code to acknowledge receipt
  • Up to 100 delivery logs stored per webhook

Best Practices​

  • Always verify signatures — never trust unverified webhook payloads
  • Respond quickly (within 5 seconds) — process asynchronously if needed
  • Return 200 immediately, then process the event in the background
  • Handle duplicates — webhooks may be delivered more than once in edge cases
  • Use HTTPS — webhook URLs should always use HTTPS
  • Monitor delivery logs — check for failures regularly in the portal