Skip to content

Webhooks

Open an app and choose Webhooks, then Add webhook with a URL and the events you want.

The URL must be public HTTPS. Discord and Slack webhook URLs get a readable message; any other URL gets JSON.

Event When
user.register A password account was created
user.login A password sign-in succeeded
user.login_failed A password sign-in failed
license.login A key sign-in succeeded
license.redeemed A key was added to an account
license.created Keys were generated (panel, API, store)
license.revoked / license.unrevoked A key was revoked or restored
license.deleted A key was deleted
user.banned / user.unbanned A user was banned or unbanned
user.deleted A user was deleted
user.renamed A username changed
user.paused A user was paused
hwid.reset A PC lock was reset
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: licensing-api-webhooks/1
X-Webhook-Timestamp: 1794497606
X-Webhook-Signature: sha256=5b1f…
{"event":"license.created","app":"My App","timestamp":1794497606,"data":{"batch_id":"bat_…","quantity":2,"preset":"month","tier":"Default"}}

Deliveries time out after 5 seconds and are not retried.

When you add a webhook, the panel shows its signing secret once (base64). The signature is HMAC-SHA256 over timestamp + "." + raw body, keyed with the decoded secret, in hex:

import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, timestamp, header, secretBase64) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; // reject old deliveries
const expected = "sha256=" + createHmac("sha256", Buffer.from(secretBase64, "base64"))
.update(`${timestamp}.${rawBody}`)
.digest("hex");
return expected.length === header.length && timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}

Always check it against the raw body, before parsing the JSON.

A manager with Webhooks: Own can add webhooks in any app they can reach. Those webhooks are told only about keys that manager generated and the users who redeemed them; bulk actions send them just their part. The owner sees every webhook, who added it and what it hears.

Such a webhook goes quiet as soon as its creator is disabled, loses the permission or loses the app, and it is deleted with them.