With webhooks, you can have external systems notified in real-time about events in Fynn. Webhooks are sent as POST requests to your registered URL and contain structured JSON data about the respective event.
Webhooks can be configured under Settings > Webhooks.
Security: X-Webhook-Signature
Each webhook request contains an X-Webhook-Signature header, which is generated as follows:
hash_hmac('sha256', $body, $webhookSecret)
$body: The unchanged JSON body of the webhook request (as a string)
$webhookSecret: Your secret key for this webhook
You can find the secret under:
Settings > Webhooks > Details
We strongly recommend validating the signature to ensure the request actually comes from Fynn.
Examples
$webhookSecret = 'your_webhook_secret';
$body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expectedSignature = hash_hmac('sha256', $body, $webhookSecret);
if (!hash_equals($expectedSignature, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
// Processing ...
http_response_code(200);
echo 'OK';
using System.Security.Cryptography;
using System.Text;
string body = await new StreamReader(Request.Body).ReadToEndAsync();
string signature = Request.Headers["X-Webhook-Signature"];
string secret = "your_webhook_secret";
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var expected = BitConverter.ToString(hash).Replace("-", "").ToLower();
if (expected != signature)
{
return Results.Unauthorized();
}
}
// Processing ...
return Results.Ok();
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use(express.text({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const webhookSecret = 'your_webhook_secret';
const signature = req.headers['x-webhook-signature'];
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(req.body)
.digest('hex');
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
// Processing ...
res.send('OK');
});
Supported Events
Product
product.created
product.updated
product.deleted
product.archived
Discount Codes
coupon.created
coupon.updated
coupon.deleted
coupon.toggled
Customers
customer.created
customer.updated
customer.archived
customer.unarchived
customer.deleted
customer.merged — Triggered when multiple customers have been merged into one.
customer.parent.assigned — Triggered when a parent customer was assigned in the organization hierarchy.
customer.parent.removed — Triggered when a parent customer was removed from the organization hierarchy.
payment_method.created
payment_method.updated
Invoices
invoice.created
invoice.finalized — Triggered after the invoice is finalized and payment processing has been initialized. The invoice will have status STATUS_UNPAID at this point.
invoice.credited
invoice.updated
invoice.refunded
invoice.canceled
invoice.paid
invoice.closed
invoice.unpaid
invoice.remindered
invoice.requires_approval
invoice.payment_method.updated
invoice.pdf.generation_requested
Price Plans
Subscriptions
subscription.activated
subscription.billed
subscription.created
subscription.changed
subscription.invoice_address.updated
subscription.delivery_address.updated
subscription.payment_method.updated
subscription.item.instant_metered
subscription.item.metered
subscription.item.quantity_changed
subscription.products.ordered
subscription.trial_expired
subscription.trial_extended
subscription.updated
subscription.paused
subscription.voided
subscription.resumed
subscription.canceled
subscription.cancellation_revoked
subscription.terminated
subscription.phase.created
subscription.phase.completed
subscription.transferred
subscription_item.terminated
Dunning
dunning_document.created
dunning_document.paid
dunning_document.canceled
Cart
Bank Accounts
Features & Entitlements
feature.created
feature.activated
feature.archived
entitlement.state.updated
Notes
Events are retried up to 3 times in case of an error.