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