Skip to content

Application webhooks (TenancyEngine)

Receive real-time event notifications when tenant lifecycle events occur in your application — tenants created, deleted, entitlements changed, feature flags toggled, and more.

Console: Application workspace → Webhooks (/applications/:id/webhooks)

Requires Applications configure to create, update, or delete endpoints. Read-only access shows existing endpoints.

How it works

When an event occurs (e.g. tenant.created), TenancyEngine queues a signed HTTP POST to every active endpoint configured for that application. Deliveries are retried with exponential back-off (10 s → 1 min → 5 min → 30 min → 2 h → 24 h) for up to 6 attempts. After 6 failures the delivery is dead-lettered.

Adding an endpoint

  1. Click Add endpoint.
  2. Enter the HTTPS URL that will receive events.
  3. Optionally narrow the subscription — enter a comma-separated list of event types (e.g. tenant.created,tenant.deleted) or leave as * to receive all events.
  4. Click Create endpoint — your signing secret is displayed once. Copy it before dismissing.

Verifying signatures

Every delivery includes two headers:

  • X-TE-Event — the event type (e.g. tenant.created)
  • X-TE-Signaturet={unixTimestamp},v1={hexHmac} (Stripe-style)

The signature is HMAC-SHA256(secret, "{timestamp}.{rawBody}") — note the timestamp is prefixed onto the payload before hashing, and verification must use the raw request body string exactly as received (re-serializing a parsed object will not match). Compare using a constant-time comparison to avoid timing attacks.

Also check the timestamp's age, not just the HMAC. A signature with a correct HMAC but a stale t= is a replayed request — someone captured a valid delivery earlier (from a proxy log, a compromised intermediary, etc.) and is resending it now. Reject any signature whose timestamp is more than 5 minutes old (or in the future); every SDK helper below does this for you by default.

The easiest way to verify correctly is the WebhookSignatureVerifier helper in TenancyEngine.Sdk (.NET, packages/TenancyEngine.Sdk), verifyWebhookSignature() in @tenancy-engine/sdk (TypeScript, packages/tenancy-engine-sdk), or VerifyWebhookSignature() in the Go module (packages/tenancyengine-go) — all three reject a signature whose timestamp is more than 5 minutes old (or in the future) by default, and all three accept an optional wider/narrower window if your receiver has a specific reason to need one:

csharp
using TenancyEngine.Sdk;

var isValid = WebhookSignatureVerifier.Verify(rawBody, request.Headers["X-TE-Signature"], secret);
// Optional 4th argument overrides the default 5-minute replay window:
// WebhookSignatureVerifier.Verify(rawBody, header, secret, TimeSpan.FromMinutes(10));

If you're hand-rolling verification instead, the equivalent — including the replay-window check — is:

csharp
var parts = signatureHeader.Split(',').Select(p => p.Split('=')).ToDictionary(p => p[0], p => p[1]);
var timestamp = long.Parse(parts["t"]);
var receivedSig = parts["v1"];

// Reject stale or future-dated signatures BEFORE comparing the HMAC — this is what stops a
// captured valid signature from being replayed indefinitely.
var age = DateTimeOffset.UtcNow - DateTimeOffset.FromUnixTimeSeconds(timestamp);
if (age < TimeSpan.Zero || age > TimeSpan.FromMinutes(5))
    return false;

var expectedSig = Convert.ToHexString(
    HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes($"{timestamp}.{rawBody}"))
).ToLowerInvariant();

var valid = CryptographicOperations.FixedTimeEquals(
    Convert.FromHexString(expectedSig),
    Convert.FromHexString(receivedSig));

Rotating a secret

Click Rotate secret on an endpoint to invalidate the current signing key and generate a new one. The new secret is shown once — update your receiver before dismissing.

Delivery log

Click Deliveries on an endpoint to see the last 50 delivery attempts — event type, HTTP status code, attempt count, and next retry time.

Disabling an endpoint

Edit an endpoint and uncheck Active to pause deliveries without deleting the endpoint. Reactivate at any time.

TenancyEngine platform documentation