License API
Every purchase (and every Tebex sale you map) issues a key like VF-7K2QD-…. Your plugin can check it against this endpoint. It's public, rate-limited to 60 requests per minute per IP, and never returns buyer emails.
Validate a key
POST /api/v1/licenses/validate
Content-Type: application/json
{ "key": "VF-XXXXX-XXXXX-XXXXX-XXXXX", "productId": "<your resource id>" }
200 OK
{ "valid": true, "productId": "…", "product": "Lifesteal Core", "owner": "Steve", "source": "marketplace", "issuedAt": "…" }
{ "valid": false, "reason": "unknown" | "revoked" | "wrong_product" | "malformed" }Example (Java)
// Paper/Spigot onEnable(): check once at startup, async.
HttpClient http = HttpClient.newHttpClient();
String body = "{\"key\":\"" + getConfig().getString("license") + "\",\"productId\":\"YOUR_ID\"}";
HttpRequest req = HttpRequest.newBuilder(URI.create("https://valtriyak.example/api/v1/licenses/validate"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body)).build();
http.sendAsync(req, HttpResponse.BodyHandlers.ofString()).thenAccept(res -> {
if (!res.body().contains("\"valid\":true")) {
getLogger().severe("Invalid license key — disabling.");
Bukkit.getScheduler().runTask(this, () -> getServer().getPluginManager().disablePlugin(this));
}
});Seller webhooks
Set a URL under Seller → Webhooks & Tebex. Failed deliveries retry with exponential backoff, up to 8 attempts. Reject requests whose timestamp is more than 5 minutes old.
POST <your webhook URL>
X-VF-Event: license.created
X-VF-Signature: t=1760000000,v1=<hex HMAC-SHA256(secret, "1760000000." + rawBody)>
{ "event": "license.created", "created": "…",
"data": { "license": "VF-…", "productId": "…", "orderId": "…", "buyer": "Steve", "amountCents": 1499, "netCents": 1409, "currency": "USD" } }Tebex
Point a Tebex webhook at the URL shown in your seller dashboard and map package IDs to resources. payment.completed issues keys; payment.refunded and payment.dispute.opened revoke them.