Webhooks reference
Oxrion Desktop can notify your own system whenever you create a license — for example to store it in a customer portal and make it downloadable. You set up webhooks in Email & Hooks.
The request
Section titled “The request”Oxrion Desktop sends an HTTP POST with a JSON body to your webhook URL:
POST /oxrion/webhook HTTP/1.1Content-Type: application/jsonUser-Agent: Oxrion-DesktopX-Oxrion-Event: license.createdX-Oxrion-Delivery: 5c1d0a3f9e2b4c7d8a6f1e0b2c3d4e5fX-Oxrion-Timestamp: 1760000000X-Oxrion-Signature: sha256=5a5a97fab1a6e646fff8630422bc96efaaeb16fbf411ef5903bbe6f5d29f80a9| Header | Meaning |
|---|---|
X-Oxrion-Event | The event. Currently always license.created. |
X-Oxrion-Delivery | A random ID, different for every attempt. Useful in your logs. |
X-Oxrion-Timestamp | When the request was sent, in Unix seconds. |
X-Oxrion-Signature | sha256= and the HMAC-SHA256 of the timestamp and the body. Only sent when the webhook has a signing secret. |
The body
Section titled “The body”{ "event": "license.created", "test": false, "id": "a1b2c3d4e5f60718293a4b5c", "host": "shop.example.com", "expiry": "2027-10-01T12:00:00+03:00", "duration": "1 year", "issued_at": "2026-10-01T09:30:00Z", "customer": "Acme Ltd.", "email": "billing@acme.example", "note": "Invoice 1042", "group": "MyProject", "fingerprint": "95867194943b3037ab0863e400e74686", "filename": "4dd390c7e80bd49479aa883814db249106596746.lic", "license": "# Host: shop.example.com\n# Expiry: 2027-10-01 12:00 (UTC+3)\n\n…"}| Field | Meaning |
|---|---|
event | license.created |
test | true for Send test webhook — sample data, not a real license. |
id | A stable ID for this license. It stays the same when the same license is sent again, so you can ignore duplicates. |
host | The license’s host. |
expiry | When the license expires, as an RFC 3339 time in the time zone it was issued in (for example 2027-10-01T12:00:00+03:00), or an empty string if it never expires. Any RFC 3339 parser reads it as an exact moment. |
duration | The period used to set the expiry, for example 1 year. Left out when the date was picked directly. |
issued_at | When the license was created (UTC). |
customer, email, note | From the license record. Left out when empty. |
group | The group’s name, if it has one. |
fingerprint | Identifies the license key that signed the license (the group). |
filename | The .lic file name. Save the file under exactly this name. |
license | The complete .lic file content. Left out if the webhook doesn’t include the license. |
Responding
Section titled “Responding”Answer with any 2xx status within 15 seconds. Anything else counts as a failure:
- Other status codes, timeouts and connection errors fail the delivery.
- Redirects are not followed. Use the final URL.
- Failed deliveries are not retried automatically. The license is still created; its row in License History shows Hook not sent, and you can send it again from there.
Verifying the signature
Section titled “Verifying the signature”With a signing secret, check every request before trusting it:
- Take the raw request body, exactly as received.
- Compute
HMAC-SHA256(secret, timestamp + "." + body)as hex and putsha256=in front. - Compare it with
X-Oxrion-Signatureusing a constant-time comparison. - Reject requests whose timestamp is more than a few minutes old, so a captured request can’t be replayed.
Use the secret exactly as it is shown in the Vault — the text itself.
<?php$secret = getenv('OXRION_WEBHOOK_SECRET');$body = file_get_contents('php://input');$timestamp = $_SERVER['HTTP_X_OXRION_TIMESTAMP'] ?? '';$signature = $_SERVER['HTTP_X_OXRION_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
if (!hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) { http_response_code(401); exit;}
$event = json_decode($body, true);
if ($event['test']) { http_response_code(204); // test delivery: nothing to store exit;}
// Store each license once, even if it is sent again.// save_license($event['id'], $event['host'], $event['filename'], $event['license'] ?? null);
http_response_code(204);Good to know
Section titled “Good to know”- Duplicates happen. A license can be sent more than once (for example when you press Send webhook again). Use
idto store it only once. - The license file is ready to use. If you offer it for download, serve
licenseunder the name infilenameso it works without renaming. - Customer data is in the body. Keep your endpoint on HTTPS and don’t log bodies you don’t need.