> ## Documentation Index
> Fetch the complete documentation index at: https://infisical.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Certificate alerts by webhook

> Send certificate alert notifications to a webhook URL.

A webhook channel on a certificate [alert](https://infisical.com/docs/documentation/platform/pki/applications/alerting/overview) sends an HTTP POST request to your endpoint whenever the alert fires. Use a webhook to connect certificate alerts to your own systems or to services Infisical doesn't support directly.

> [!NOTE]
>
> Webhook channels are available under Infisical's [Enterprise](https://infisical.com/pricing) plan. If you're self-hosting Infisical, contact [sales@infisical.com](mailto:sales@infisical.com) to purchase an enterprise license.

## Prerequisites

* The Certificate Management **Admin** role to create an organization-wide alert, or the **Admin** [application role](https://infisical.com/docs/documentation/platform/pki/applications/overview.md#application-roles) to create an application alert
* An HTTPS endpoint that accepts POST requests

## Create the alert

> [tab] Infisical UI
>
> **Step 1**
>
> Open the alert form:
>
> * For an organization-wide alert, go to **Certificate Manager** > **Settings**, select the **Alerts** tab, then select **Create Alert**
> * For an application alert, go to **Certificate Manager** > **Applications** and select your application, then select the **Settings** tab and select **Create Alert** in the **Alerts** section
>
> **Step 2**
>
> Select the **Alert Type**, then enter an **Alert Name** and an optional **Description**. An application can have one alert for each event, so to notify more places about the same event, add more channels to that alert.
>
> **Step 3**
>
> For an expiration alert, enter **Alert Before** as a number and a unit. For example: `30d` or `2w`, up to 365 days.
>
> > [!NOTE]
> >
> > To get a reminder every day, turn on **Repeat daily until expiry**. Otherwise, the alert repeats on a [schedule based on **Alert Before**](https://infisical.com/docs/documentation/platform/pki/applications/alerting/overview#how-often-expiration-alerts-repeat).
>
> Select **Continue**.
>
> **Step 4**
>
> For an organization-wide alert, on the **Filters** step, optionally select **Add Filter** to [narrow the certificates the alert covers](https://infisical.com/docs/documentation/platform/pki/applications/alerting/overview#filter-an-organization-wide-alert), then select **Continue**. Application alerts and signer certificate expiration alerts skip this step.
>
> > [!TIP]
> >
> > To add this channel to an existing alert instead, edit the alert and go to its **Channels** step.
>
> On the **Channels** step, add the webhook channel:
>
> **Step 1**
>
> Select **Add Channel** > **Webhook** and enter your endpoint's **URL**. The URL must use HTTPS.
>
> **Step 2**
>
> If your endpoint requires authentication, under **Authentication**, select **Bearer Token**, **Basic Auth**, or **API Key**, and enter the [credentials](#authentication) it expects.
>
> **Step 3**
>
> Optionally, select **Add Signing Secret** and enter a secret, so your endpoint can [verify the signature](#verify-the-signature) of each request.
>
> **Step 4**
>
> Optionally, select **Add Header** to send [custom headers](#custom-headers) with each request.
>
> **Step 5**
>
> Optionally, select **Send test** to send a test request to your endpoint.
>
> **Step 6**
>
> Select **Continue**, check the **Review** step, and select **Create Alert**.

> [tab] API
>
> Send a request to the create alert endpoint with an [access token](https://infisical.com/docs/api-reference/overview/authentication.md) and a `webhook` channel.
>
> Set `resourceType` and `resourceId` to the certificates the alert covers:
>
> | Alert | `resourceType` | `resourceId` |
> | - | - | - |
> | Organization-wide | `cert-manager` | Leave out |
> | Application | `cert-manager.application` | The application's ID, from [the list applications endpoint](https://infisical.com/docs/api-reference/endpoints/pki/applications/configuration/list.md) |
> | Signer certificate expiration | `cert-manager.signer` | Leave out |
>
> Set `eventType` to the event the alert watches:
>
> | Event | Organization-wide alert | Application alert |
> | - | - | - |
> | Expiration | `cert-manager.certificate.expiry` | `cert-manager.application.certificate.expiry` |
> | Issuance | `cert-manager.certificate.issuance` | `cert-manager.application.certificate.issuance` |
> | Renewal | `cert-manager.certificate.renewal` | `cert-manager.application.certificate.renewal` |
> | Revocation | `cert-manager.certificate.revocation` | `cert-manager.application.certificate.revocation` |
> | Signer certificate expiration | `cert-manager.signer.certificate.expiry` | Not available |
>
> Set `condition` to the same settings as the **Alert Before** field, the **Repeat daily until expiry** toggle, and the **Filters** step in Infisical:
>
> | Field | Applies to | Value |
> | - | - | - |
> | `alertBefore` | Expiration alerts (required) | A number and a unit, such as `30d` or `2w`, up to 365 days |
> | `dailyReminder` | Expiration alerts | `true` to [repeat the alert every day](https://infisical.com/docs/documentation/platform/pki/applications/alerting/overview#how-often-expiration-alerts-repeat) until the certificate expires |
> | `applicationIds` | Organization-wide alerts | Up to 100 application IDs |
> | `profileIds` | Organization-wide alerts | Up to 100 certificate profile IDs |
> | `sources` | Organization-wide alerts | Any of `issued` (**Managed** in Infisical), `imported`, and `discovered` |
> | `signerIds` | Signer certificate expiration alerts | Up to 100 signer IDs |
>
> For issuance, renewal, and revocation alerts on an application, leave out `condition`.
>
> The channel's `config` takes these fields:
>
> | Field | Value |
> | - | - |
> | `url` (required) | Your endpoint's HTTPS URL |
> | `authType` | `bearer` (**Bearer Token**), `basic` (**Basic Auth**), or `header` (**API Key**), for [authentication](#authentication) |
> | `authCredentials` | The token, if `authType` is `bearer` |
> | `basicUsername`, `basicPassword` | The username and password, if `authType` is `basic` |
> | `authHeaderName`, `authHeaderValue` | The header name and the API key, if `authType` is `header` |
> | `signingSecret` | A secret to [sign each request](#verify-the-signature) with |
> | `headers` | [Custom headers](#custom-headers), as an array of `{ "key": "<name>", "value": "<value>" }` objects |
>
> ```bash Organization-wide theme={"dark"}
> curl --request POST \
>   --url https://app.infisical.com/api/v1/alerts \
>   --header 'Authorization: Bearer <access-token>' \
>   --header 'Content-Type: application/json' \
>   --data '{
>     "name": "imported-certificates-expiring",
>     "resourceType": "cert-manager",
>     "eventType": "cert-manager.certificate.expiry",
>     "condition": { "alertBefore": "30d", "sources": ["imported"] },
>     "channels": [
>       {
>         "name": "certificate-inventory",
>         "channelType": "webhook",
>         "config": {
>           "url": "https://alerts.example.com/infisical",
>           "authType": "bearer",
>           "authCredentials": "<token>",
>           "signingSecret": "<signing-secret>"
>         }
>       }
>     ]
>   }'
> ```
>
> ```bash Application theme={"dark"}
> curl --request POST \
>   --url https://app.infisical.com/api/v1/alerts \
>   --header 'Authorization: Bearer <access-token>' \
>   --header 'Content-Type: application/json' \
>   --data '{
>     "name": "payments-api-renewals",
>     "resourceType": "cert-manager.application",
>     "resourceId": "<application-id>",
>     "eventType": "cert-manager.application.certificate.renewal",
>     "channels": [
>       {
>         "name": "payments-deploy-hook",
>         "channelType": "webhook",
>         "config": {
>           "url": "https://deploy.example.com/hooks/certificate-renewed",
>           "signingSecret": "<signing-secret>",
>           "headers": [{ "key": "X-Env", "value": "production" }]
>         }
>       }
>     ]
>   }'
> ```

> [!TIP]
>
> Your alert is set up. Infisical sends a POST request to your endpoint whenever the alert fires.

## Authentication

A channel can have one authentication method, which Infisical sends as a header on each request:

| Type | What you enter | Header that Infisical sends |
| - | - | - |
| **Bearer Token** | A token | `Authorization: Bearer <token>` |
| **Basic Auth** | A username and a password | `Authorization: Basic <base64 of username:password>` |
| **API Key** | A header name, such as `X-API-Key`, and the key | `<header name>: <key>` |

For an `Authorization` scheme other than `Bearer`, such as `GenieKey <key>`, select **API Key**, enter `Authorization` as the header name, and enter the full value, including the scheme.

Infisical doesn't show saved credentials again. When you edit the channel, leave a field blank to keep its saved value. If you change the URL, enter the credentials again, because Infisical only sends them to the URL they were saved with.

## Custom headers

Infisical sends custom headers with each request, such as an `X-Env` header your endpoint uses to route alerts:

* Anyone who can view the alert can see header values, so put credentials, including any `Authorization` header, in [authentication](#authentication)
* A channel can have up to 20 custom headers, and header names, values, and authentication credentials can total up to 4,096 characters
* Infisical sets `Content-Type`, `Content-Length`, `Transfer-Encoding`, `Connection`, `Host`, and `x-infisical-signature` itself, so you can't add them

## Webhook event types

Each request is a [CloudEvents](https://cloudevents.io/) JSON payload. The `type` field names the event, and application alerts and organization-wide alerts use different values:

| Event | Application alert `type` | Organization-wide alert `type` |
| - | - | - |
| Expiration | `com.infisical.cert-manager.application.certificate.expiry` | `com.infisical.cert-manager.certificate.expiry` |
| Issuance | `com.infisical.cert-manager.application.certificate.issuance` | `com.infisical.cert-manager.certificate.issuance` |
| Renewal | `com.infisical.cert-manager.application.certificate.renewal` | `com.infisical.cert-manager.certificate.renewal` |
| Revocation | `com.infisical.cert-manager.application.certificate.revocation` | `com.infisical.cert-manager.certificate.revocation` |
| Signer certificate expiration | Not available | `com.infisical.cert-manager.signer.certificate.expiry` |

The `subject` field is the same value without the `com.infisical.` prefix.

## Webhook payload format

`data.items` lists the certificates the alert fired for. Each item has the certificate's ID, a `title` (its common name, or its first SAN or serial number when it has none), a one-line `summary`, a `severity`, and a list of display `fields`. To act on a certificate in code, read `resource` instead of `fields`: it holds the certificate's typed values, with ISO 8601 dates and the raw revocation reason code. An expiration request can include several certificates, and `data.alert.condition` holds the alert's lead time as you entered it, such as `30d` or `2w`. Issuance, renewal, and revocation requests include the one certificate the event was about.

To find out which kind of alert sent a request, read `data.alert.resourceType`:

* `cert-manager.application`: an application alert, with the application's ID in `data.alert.resourceId`
* `cert-manager`: an organization-wide alert
* `cert-manager.signer`: a signer certificate expiration alert, whose items list the signers in `resource.signerIds` and `resource.signerNames` (only the selected signers, if the alert has a **Signers** filter)

> [!NOTE]
>
> Alerts created before certificate alerts moved to applications keep sending the older `com.infisical.pki.certificate.*` event types with a `data.certificates` list. If one endpoint receives both, check the `type` field to tell them apart.

> [tab] Expiration
>
> ```json expandable theme={"dark"}
> {
>   "specversion": "1.0",
>   "type": "com.infisical.cert-manager.application.certificate.expiry",
>   "source": "/applications/<application-id>/alerts/<alert-id>",
>   "id": "<unique-event-id>",
>   "time": "2026-09-24T00:00:09.664Z",
>   "datacontenttype": "application/json",
>   "subject": "cert-manager.application.certificate.expiry",
>   "data": {
>     "alert": {
>       "id": "<alert-id>",
>       "name": "tls-expiry-alert",
>       "resourceType": "cert-manager.application",
>       "resourceId": "<application-id>",
>       "condition": "30d"
>     },
>     "items": [
>       {
>         "id": "<certificate-id>",
>         "title": "api.example.com",
>         "summary": "Certificate 'api.example.com' in application 'payments-api' expires on October 4, 2026 at 11:59 PM UTC",
>         "severity": "error",
>         "fields": [
>           { "label": "Serial Number", "value": "105d3b4c28132774ab17e350c0e8229ba1bef40d" },
>           { "label": "SANs", "value": "api.example.com, www.api.example.com" },
>           { "label": "Profile", "value": "tls-server" },
>           { "label": "Expires", "value": "October 4, 2026 at 11:59 PM UTC" }
>         ],
>         "resource": {
>           "id": "<certificate-id>",
>           "serialNumber": "105d3b4c28132774ab17e350c0e8229ba1bef40d",
>           "commonName": "api.example.com",
>           "altNames": ["api.example.com", "www.api.example.com"],
>           "status": "active",
>           "notBefore": "2026-07-06T23:59:00.000Z",
>           "notAfter": "2026-10-04T23:59:00.000Z",
>           "revokedAt": null,
>           "revocationReason": null,
>           "profileId": "<profile-id>",
>           "profileName": "tls-server",
>           "applicationId": "<application-id>",
>           "applicationName": "payments-api"
>         }
>       }
>     ],
>     "metadata": {
>       "totalItems": 1,
>       "viewUrl": "https://app.infisical.com/organizations/<org-id>/cert-manager/applications/<application-name>"
>     }
>   }
> }
> ```

> [tab] Revocation
>
> Revocation items also include a **Revocation Reason** field. Issuance and renewal requests have the same shape without that field.
>
> ```json expandable theme={"dark"}
> {
>   "specversion": "1.0",
>   "type": "com.infisical.cert-manager.application.certificate.revocation",
>   "source": "/applications/<application-id>/alerts/<alert-id>",
>   "id": "<unique-event-id>",
>   "time": "2026-09-24T14:22:00.000Z",
>   "datacontenttype": "application/json",
>   "subject": "cert-manager.application.certificate.revocation",
>   "data": {
>     "alert": {
>       "id": "<alert-id>",
>       "name": "prod-revocation-notify",
>       "resourceType": "cert-manager.application",
>       "resourceId": "<application-id>"
>     },
>     "items": [
>       {
>         "id": "<certificate-id>",
>         "title": "api.example.com",
>         "summary": "Certificate 'api.example.com' was revoked in application 'payments-api'",
>         "severity": "warning",
>         "fields": [
>           { "label": "Serial Number", "value": "105d3b4c28132774ab17e350c0e8229ba1bef40d" },
>           { "label": "Profile", "value": "tls-server" },
>           { "label": "Expires", "value": "October 4, 2026 at 11:59 PM UTC" },
>           { "label": "Revocation Reason", "value": "Key Compromise" }
>         ],
>         "resource": {
>           "id": "<certificate-id>",
>           "serialNumber": "105d3b4c28132774ab17e350c0e8229ba1bef40d",
>           "commonName": "api.example.com",
>           "altNames": [],
>           "status": "revoked",
>           "notBefore": "2026-07-06T23:59:00.000Z",
>           "notAfter": "2026-10-04T23:59:00.000Z",
>           "revokedAt": "2026-09-24T14:21:58.000Z",
>           "revocationReason": 1,
>           "profileId": "<profile-id>",
>           "profileName": "tls-server",
>           "applicationId": "<application-id>",
>           "applicationName": "payments-api"
>         }
>       }
>     ],
>     "metadata": {
>       "totalItems": 1,
>       "viewUrl": "https://app.infisical.com/organizations/<org-id>/cert-manager/applications/<application-name>"
>     }
>   }
> }
> ```

> [tab] Organization-wide alert
>
> An organization-wide alert sends the same payload as an application alert, with `resourceType` set to `cert-manager`. This example shows a revocation:
>
> ```json expandable theme={"dark"}
> {
>   "specversion": "1.0",
>   "type": "com.infisical.cert-manager.certificate.revocation",
>   "source": "/alerts/453421e2-2e73-4700-92c8-60ccd3e7073e",
>   "id": "8360c717-097a-4592-a2b0-63c494c3b558",
>   "time": "2026-10-05T21:37:35.935Z",
>   "datacontenttype": "application/json",
>   "subject": "cert-manager.certificate.revocation",
>   "data": {
>     "alert": {
>       "id": "453421e2-2e73-4700-92c8-60ccd3e7073e",
>       "name": "prod-revocations",
>       "resourceType": "cert-manager"
>     },
>     "items": [
>       {
>         "id": "4f70e08e-ad7a-4f94-ac16-241126ab88ed",
>         "title": "api.example.com",
>         "summary": "Certificate 'api.example.com' was revoked in application 'payments-api'",
>         "severity": "warning",
>         "fields": [
>           { "label": "Serial Number", "value": "77c2b1368a7dd4a8442c61d51d20cbdb2060a1e5" },
>           { "label": "Profile", "value": "tls-server" },
>           { "label": "Application", "value": "payments-api" },
>           { "label": "Expires", "value": "November 4, 2026 at 09:37 PM UTC" },
>           { "label": "Revocation Reason", "value": "Unspecified" }
>         ],
>         "resource": {
>           "id": "4f70e08e-ad7a-4f94-ac16-241126ab88ed",
>           "serialNumber": "77c2b1368a7dd4a8442c61d51d20cbdb2060a1e5",
>           "commonName": "api.example.com",
>           "altNames": [],
>           "status": "revoked",
>           "notBefore": "2026-10-05T21:37:28.426Z",
>           "notAfter": "2026-11-04T21:37:28.426Z",
>           "revokedAt": "2026-10-05T21:37:30.299Z",
>           "revocationReason": 0,
>           "profileId": "8d04ef1b-67cd-4021-8da4-b3096d11133b",
>           "profileName": "tls-server",
>           "applicationId": "d6e7232b-ffa5-48bf-9133-4731c6d001a0",
>           "applicationName": "payments-api"
>         }
>       }
>     ],
>     "metadata": {
>       "totalItems": 1,
>       "viewUrl": "https://app.infisical.com/organizations/<org-id>/cert-manager/inventory"
>     }
>   }
> }
> ```

> [tab] Signer certificate expiration
>
> ```json expandable theme={"dark"}
> {
>   "specversion": "1.0",
>   "type": "com.infisical.cert-manager.signer.certificate.expiry",
>   "source": "/alerts/9b3c1d2e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
>   "id": "2c4e6a8b-1d3f-4a5c-9e7b-0d2f4a6c8e1b",
>   "time": "2026-10-05T00:05:00.000Z",
>   "datacontenttype": "application/json",
>   "subject": "cert-manager.signer.certificate.expiry",
>   "data": {
>     "alert": {
>       "id": "9b3c1d2e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
>       "name": "signer-expiry",
>       "resourceType": "cert-manager.signer"
>     },
>     "items": [
>       {
>         "id": "7e1f3a5c-9b2d-4e6f-8a0c-1d3e5f7a9b2c",
>         "title": "release signing",
>         "summary": "Certificate 'release signing' of signer 'release-signer' expires on October 25, 2026 at 12:00 AM UTC",
>         "severity": "warning",
>         "fields": [
>           { "label": "Signer", "value": "release-signer" },
>           { "label": "Serial Number", "value": "0a1b2c3d4e5f60718293a4b5c6d7e8f901234567" },
>           { "label": "Expires", "value": "October 25, 2026 at 12:00 AM UTC" }
>         ],
>         "resource": {
>           "id": "7e1f3a5c-9b2d-4e6f-8a0c-1d3e5f7a9b2c",
>           "serialNumber": "0a1b2c3d4e5f60718293a4b5c6d7e8f901234567",
>           "commonName": "release signing",
>           "altNames": [],
>           "status": "active",
>           "notBefore": "2025-10-25T00:00:00.000Z",
>           "notAfter": "2026-10-25T00:00:00.000Z",
>           "signerIds": ["3d5f7a9b-2c4e-4f6a-8b0d-1e3f5a7c9e2b"],
>           "signerNames": ["release-signer"]
>         }
>       }
>     ],
>     "metadata": {
>       "totalItems": 1,
>       "viewUrl": "https://app.infisical.com/organizations/<org-id>/cert-manager/code-signing"
>     }
>   }
> }
> ```

## Webhook signature verification

If you set a signing secret, each request carries an `x-infisical-signature` header:

```
x-infisical-signature: t=<timestamp>,v1=<signature>
```

| Component | Description |
| - | - |
| `t=<timestamp>` | Unix timestamp in milliseconds when Infisical signed the request |
| `v1=<signature>` | Hex-encoded HMAC SHA256 signature |

### Verify the signature

1. Read the timestamp and the signature from the header
2. Join the timestamp and the raw request body with a dot: `{timestamp}.{raw-body}`
3. Compute the HMAC SHA256 of that string with your signing secret
4. Compare the result with the signature from the header

```javascript theme={"dark"}
const crypto = require('crypto');

function verifyWebhookSignature(header, body, secret) {
  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)
  );
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.