docs/documentation/platform/pki/alerting/webhook-alerts.mdx
Infisical can send PKI certificate alert notifications to a webhook URL. This guide walks through creating a webhook alert, the payload format, and how to verify webhook signatures.
To create an alert, head to your Certificate Management Project > Alerting and press Create Certificate Alert.
Here's some guidance for each field in the alert configuration sequence:
tls-expiry-alert.30d.example.com by setting the field to Common Name, the operator to Contains, and the value to example.com.Next, add a Webhook notification channel. The URL must use HTTPS. Optionally configure a signing secret to verify the authenticity of webhook payloads.
Each alert type maps to a corresponding CloudEvents event type:
| Alert Type | Event Type | Subject |
|---|---|---|
| Certificate Expiration | com.infisical.pki.certificate.expiration | certificate-expiration-alert |
| Certificate Issuance | com.infisical.pki.certificate.issuance | certificate-issuance-alert |
| Certificate Renewal | com.infisical.pki.certificate.renewal | certificate-renewal-alert |
| Certificate Revocation | com.infisical.pki.certificate.revocation | certificate-revocation-alert |
Webhook notifications are sent as HTTP POST requests with a CloudEvents compliant JSON payload.
{
"specversion": "1.0",
"type": "com.infisical.pki.certificate.expiration",
"source": "/projects/<project-id>/alerts/<alert-id>",
"id": "<unique-event-id>",
"time": "2024-01-15T10:30:00.000Z",
"datacontenttype": "application/json",
"subject": "certificate-expiration-alert",
"data": {
"alert": {
"id": "<alert-id>",
"name": "tls-expiry-alert",
"alertBefore": "30d",
"projectId": "<project-id>"
},
"certificates": [
{
"id": "<certificate-id>",
"serialNumber": "1234567890",
"commonName": "example.com",
"san": ["example.com", "www.example.com"],
"profileName": "TLS Server",
"notBefore": "2024-01-01T00:00:00.000Z",
"notAfter": "2024-12-31T23:59:59.000Z",
"status": "active",
"daysUntilExpiry": 30
}
],
"metadata": {
"totalCertificates": 1,
"viewUrl": "https://app.infisical.com/cert-manager/<project-id>/policies"
}
}
}
These alerts are sent in real time the moment a certificate event occurs. Each notification contains a single certificate. The type and subject fields reflect the specific event, and alertBefore is omitted.
For revocation alerts, the certificate object also includes revokedAt and revocationReason fields.
{
"specversion": "1.0",
"type": "com.infisical.pki.certificate.issuance",
"source": "/projects/<project-id>/alerts/<alert-id>",
"id": "<unique-event-id>",
"time": "2024-06-15T14:22:00.000Z",
"datacontenttype": "application/json",
"subject": "certificate-issuance-alert",
"data": {
"alert": {
"id": "<alert-id>",
"name": "prod-issuance-notify",
"projectId": "<project-id>"
},
"certificates": [
{
"id": "<certificate-id>",
"serialNumber": "9876543210",
"commonName": "api.example.com",
"san": ["api.example.com"],
"profileName": "API Server",
"notBefore": "2024-06-15T00:00:00.000Z",
"notAfter": "2025-06-15T23:59:59.000Z",
"status": "active",
"daysUntilExpiry": 365
}
],
"metadata": {
"totalCertificates": 1,
"viewUrl": "https://app.infisical.com/cert-manager/<project-id>/policies"
}
}
}
If you configure a signing secret for your webhook channel, Infisical will include an x-infisical-signature header with each request. This allows you to verify that the webhook payload originated from Infisical.
The header format is:
x-infisical-signature: t=<timestamp>,v1=<signature>
Where:
<timestamp> is the Unix timestamp (in milliseconds) when the signature was generatedv1 indicates the signature version<signature> is the HMAC SHA256 signature of the payloadTo verify the signature:
.), and the raw request body: {timestamp}.{body}Example verification in Node.js:
const crypto = require('crypto');
function verifyWebhookSignature(header, body, secret) {
// Parse header format: t=<timestamp>,v1=<signature>
const parts = header.split(',');
const timestamp = parts[0].replace('t=', '');
const signature = parts[1].replace('v1=', '');
const signaturePayload = `${timestamp}.${body}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signaturePayload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}