diff --git a/constructs/ssl-monitor.mdx b/constructs/ssl-monitor.mdx
new file mode 100644
index 00000000..20d583e0
--- /dev/null
+++ b/constructs/ssl-monitor.mdx
@@ -0,0 +1,509 @@
+---
+title: 'SslMonitor Construct'
+description: 'Learn how to configure SSL monitors with the Checkly CLI.'
+sidebarTitle: 'SSL Monitor'
+---
+
+import GeneralMonitorOptionsTable from '/snippets/general-monitor-options-table.mdx';
+
+
+Learn more about SSL Monitors in [the SSL monitor overview](/detect/uptime-monitoring/ssl-monitors/overview).
+
+
+Use SSL Monitors to verify the health and security posture of your TLS certificates. The examples below show how to configure monitoring for common scenarios.
+
+
+Before creating SSL Monitors, ensure you have:
+
+- An initialized Checkly CLI project
+- Network access to the HTTPS endpoint you want to monitor
+- The hostname (and optionally port) of the target server
+
+For additional setup information, see [CLI overview](/cli/overview).
+
+
+
+
+```ts Basic Example
+import { Frequency, SslMonitor } from "checkly/constructs"
+
+new SslMonitor("homepage-ssl", {
+ name: "Homepage Certificate",
+ description: "Monitors the TLS certificate for `example.com`.",
+ frequency: Frequency.EVERY_1H,
+ request: {
+ hostname: "example.com",
+ sslConfig: {
+ alertDaysBeforeExpiry: 30,
+ },
+ },
+})
+```
+
+```ts Advanced Example
+import {
+ Frequency,
+ SslAssertionBuilder,
+ SslMonitor,
+ TlsVersion,
+} from "checkly/constructs"
+
+new SslMonitor("api-ssl-advanced", {
+ name: "API Certificate — strict baseline",
+ description: "Enforces TLS 1.3, checks chain trust, and alerts 45 days before expiry.",
+ activated: true,
+ frequency: Frequency.EVERY_10M,
+ locations: ["us-east-1", "eu-west-1"],
+ degradedResponseTime: 1000,
+ maxResponseTime: 5000,
+ request: {
+ hostname: "api.example.com",
+ port: 443,
+ sslConfig: {
+ alertDaysBeforeExpiry: 45,
+ handshakeTimeout: 5000,
+ securityBaseline: {
+ enabled: true,
+ minTLSVersion: { value: "TLS1.2", severity: "fail" },
+ recommendedTLSVersion: { value: "TLS1.3", severity: "degrade" },
+ weakCipherSuite: { severity: "fail" },
+ ocspMustStapleRespected: { severity: "degrade" },
+ },
+ },
+ assertions: [
+ SslAssertionBuilder.certNotExpired().equals(true),
+ SslAssertionBuilder.chainTrusted().equals(true),
+ SslAssertionBuilder.hostnameVerified().equals(true),
+ SslAssertionBuilder.tlsVersion().greaterThanOrEqual(TlsVersion.TLS1_2),
+ SslAssertionBuilder.certExpiresInDays().greaterThan(45),
+ ],
+ },
+})
+```
+
+
+
+## Configuration
+
+SSL monitors have SSL-specific settings and inherit the standard monitor options shared across all check types.
+
+
+
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `request` | `object` | ✅ | - | SSL connection and certificate configuration |
+| `degradedResponseTime` | `number` | ❌ | `3000` | Handshake time in milliseconds at which the monitor is marked degraded |
+| `maxResponseTime` | `number` | ❌ | `10000` | Handshake time in milliseconds at which the monitor is marked failed |
+
+
+
+
+
+
+
+
+
+### `SslMonitor` Options
+
+
+
+SSL connection configuration that defines the target host and all TLS-specific options.
+
+**Usage:**
+
+```ts
+new SslMonitor("my-ssl-monitor", {
+ name: "Example SSL Monitor",
+ request: {
+ hostname: "example.com",
+ sslConfig: {
+ alertDaysBeforeExpiry: 30,
+ },
+ },
+})
+```
+
+**Parameters:**
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `hostname` | `string` | ✅ | - | The hostname to connect to and validate (no scheme or port) |
+| `port` | `number` | ❌ | `443` | TCP port to connect to (1–65535) |
+| `ipFamily` | `string` | ❌ | `'IPv4'` | IP family: `'IPv4'` \| `'IPv6'` |
+| `sslConfig` | `SslConfig` | ✅ | - | TLS handshake and certificate options (see below) |
+| `assertions` | `SslAssertion[]` | ❌ | `[]` | Certificate/handshake assertions using `SslAssertionBuilder` |
+
+
+
+
+TLS handshake time in milliseconds at or above which the monitor is marked as degraded (warning state). Range: 0–30,000 ms. Must be ≤ `maxResponseTime`.
+
+**Usage:**
+
+```ts highlight={3}
+new SslMonitor("my-ssl", {
+ name: "Example SSL",
+ degradedResponseTime: 1000,
+ maxResponseTime: 5000,
+ request: {
+ hostname: "example.com",
+ sslConfig: {},
+ },
+})
+```
+
+
+
+TLS handshake time in milliseconds at or above which the monitor is marked as failed. Range: 0–30,000 ms.
+
+**Usage:**
+
+```ts highlight={4}
+new SslMonitor("my-ssl", {
+ name: "Example SSL",
+ degradedResponseTime: 1000,
+ maxResponseTime: 5000,
+ request: {
+ hostname: "example.com",
+ sslConfig: {},
+ },
+})
+```
+
+
+### `SslConfig` Options
+
+
+Raise a degraded alert when the certificate is within this many days of expiry. Range: 1–365.
+
+```ts
+sslConfig: {
+ alertDaysBeforeExpiry: 30, // Warn 30 days before expiry
+}
+```
+
+
+
+SNI server name to send during the TLS handshake. Useful when a single IP hosts multiple certificates. Defaults to `hostname` when unset.
+
+```ts
+sslConfig: {
+ serverName: "tenant-a.example.com",
+}
+```
+
+
+
+Maximum milliseconds to wait for the TLS handshake to complete. Range: 1,000–30,000 ms.
+
+```ts
+sslConfig: {
+ handshakeTimeout: 5000,
+}
+```
+
+
+
+When `true`, the certificate chain is not verified against system trusted roots. The certificate is still inspected for expiry and the security baseline. Use for internal or self-signed certificates.
+
+```ts
+sslConfig: {
+ skipChainValidation: true,
+}
+```
+
+
+
+Enables mutual TLS by sending a client certificate during the handshake.
+
+- `'auto'` — Checkly selects a stored client certificate automatically.
+- `'explicit'` — uses the certificate referenced by `sslClientCertificateId`.
+
+Omit to connect without a client certificate.
+
+```ts
+sslConfig: {
+ clientCertificateMode: "explicit",
+ sslClientCertificateId: "cert_abc123",
+}
+```
+
+
+
+The ID of the stored client certificate to present during the TLS handshake. Required when `clientCertificateMode` is `'explicit'`. Client certificates are managed under **Settings → Client Certificates** in the Checkly dashboard.
+
+```ts
+sslConfig: {
+ clientCertificateMode: "explicit",
+ sslClientCertificateId: "cert_abc123",
+}
+```
+
+
+
+Override the account-level security baseline for this monitor. Omit to inherit the account default.
+
+```ts
+sslConfig: {
+ securityBaseline: {
+ enabled: true,
+ minTLSVersion: { value: "TLS1.2", severity: "fail" },
+ recommendedTLSVersion: { value: "TLS1.3", severity: "degrade" },
+ weakCipherSuite: { severity: "fail" },
+ weakSignatureAlgorithm: { severity: "fail" },
+ knownBadCA: { severity: "fail" },
+ ocspMustStapleRespected: { severity: "degrade" },
+ },
+}
+```
+
+**`SecurityBaseline` parameters:**
+
+| Parameter | Type | Default severity | Description |
+|-----------|------|-----------------|-------------|
+| `enabled` | `boolean` | `true` | Enable or disable baseline evaluation |
+| `minTLSVersion` | `{ value?: string, severity? }` | `fail` | Minimum required TLS version (e.g. `'TLS1.2'`) |
+| `minKeySizeBits` | `{ value?: number, severity? }` | `fail` | Minimum RSA key size in bits |
+| `weakSignatureAlgorithm` | `{ severity? }` | `fail` | Reject weak signature algorithms (MD2-RSA, MD5-RSA, SHA1-RSA, DSA-SHA1, ECDSA-SHA1) on non-root certificates |
+| `weakCipherSuite` | `{ severity? }` | `fail` | Reject known-weak cipher suites |
+| `knownBadCA` | `{ severity? }` | `fail` | Reject distrusted certificate authorities |
+| `recommendedTLSVersion` | `{ value?: string, severity? }` | `ignore` | Advisory minimum TLS version |
+| `recommendedKeySizeBits` | `{ value?: number, severity? }` | `ignore` | Advisory minimum RSA key size |
+| `ocspMustStapleRespected` | `{ severity? }` | `ignore` | Alert when must-staple cert is missing an OCSP staple |
+| `sctPresent` | `{ severity? }` | `ignore` | Alert when no Signed Certificate Timestamp is observed |
+
+Each rule's `severity` can be `'fail'` \| `'degrade'` \| `'ignore'`.
+
+
+### `SslMonitor` Assertions
+
+Define `assertions` using the `SslAssertionBuilder`. The following sources are available:
+
+| Builder method | Source | Value type | Description |
+|---------------|--------|-----------|-------------|
+| `certNotExpired()` | `CERT_NOT_EXPIRED` | boolean | Certificate has not passed its expiry date |
+| `certExpiresInDays()` | `CERT_EXPIRES_IN_DAYS` | number | Days until the certificate expires |
+| `hostnameVerified()` | `HOSTNAME_VERIFIED` | boolean | Certificate SANs cover the monitored hostname |
+| `chainTrusted()` | `CHAIN_TRUSTED` | boolean | Full chain is trusted by the system root store |
+| `tlsVersion()` | `TLS_VERSION` | string | Negotiated TLS version |
+| `cipherSuite()` | `CIPHER_SUITE` | string | Negotiated IANA cipher suite name |
+| `signatureAlgorithm()` | `SIGNATURE_ALGORITHM` | string | Leaf certificate signature algorithm |
+| `keySizeBits()` | `KEY_SIZE_BITS` | number | Leaf certificate public key size in bits |
+| `issuerCn()` | `ISSUER_CN` | string | Common name of the certificate issuer |
+| `certFingerprintSha256()` | `CERT_FINGERPRINT_SHA256` | string | SHA-256 fingerprint of the leaf certificate |
+| `issuerFingerprintSha256()` | `ISSUER_FINGERPRINT_SHA256` | string | SHA-256 fingerprint of the issuer |
+| `sanContains()` | `SAN_CONTAINS` | string | Any SAN matches the given value |
+| `ocspStapled()` | `OCSP_STAPLED` | boolean | A stapled OCSP response was included in the handshake |
+| `handshakeTimeMs()` | `HANDSHAKE_TIME_MS` | number | TLS handshake duration in milliseconds |
+
+Examples:
+
+```ts
+// Certificate must not be expired
+SslAssertionBuilder.certNotExpired().equals(true)
+// Equivalent to: { source: 'CERT_NOT_EXPIRED', comparison: 'EQUALS', target: 'true' }
+
+// Alert when fewer than 30 days remain before expiry
+SslAssertionBuilder.certExpiresInDays().greaterThan(30)
+// Equivalent to: { source: 'CERT_EXPIRES_IN_DAYS', comparison: 'GREATER_THAN', target: '30' }
+
+// Require TLS 1.2 or newer
+SslAssertionBuilder.tlsVersion().greaterThanOrEqual(TlsVersion.TLS1_2)
+// Equivalent to: { source: 'TLS_VERSION', comparison: 'GREATER_THAN_OR_EQUAL', target: 'TLS1.2' }
+
+// Pin the cipher suite
+SslAssertionBuilder.cipherSuite().equals(CipherSuite.TLS_AES_256_GCM_SHA384)
+// Equivalent to: { source: 'CIPHER_SUITE', comparison: 'EQUALS', target: 'TLS_AES_256_GCM_SHA384' }
+
+// Verify a specific issuer
+SslAssertionBuilder.issuerCn().equals("Let's Encrypt")
+// Equivalent to: { source: 'ISSUER_CN', comparison: 'EQUALS', target: "Let's Encrypt" }
+
+// Require a minimum key size
+SslAssertionBuilder.keySizeBits().greaterThanOrEqual(2048)
+// Equivalent to: { source: 'KEY_SIZE_BITS', comparison: 'GREATER_THAN_OR_EQUAL', target: '2048' }
+```
+
+Use the `TlsVersion` and `CipherSuite` constants for type-safe comparisons:
+
+```ts
+import {
+ CipherSuite,
+ SslAssertionBuilder,
+ TlsVersion,
+} from "checkly/constructs"
+
+SslAssertionBuilder.tlsVersion().equals(TlsVersion.TLS1_3)
+SslAssertionBuilder.cipherSuite().equals(CipherSuite.TLS_AES_128_GCM_SHA256)
+```
+
+Learn more in our docs on [Assertions](/detect/assertions).
+
+### General Monitor Options
+
+
+Friendly name for your SSL Monitor, displayed in the Checkly dashboard and used in notifications.
+
+```ts highlight={2}
+new SslMonitor("my-ssl-monitor", {
+ name: "Homepage Certificate",
+ /* More options ... */
+})
+```
+
+
+
+How often the SSL Monitor should run. Use the `Frequency` enum to set the check interval.
+
+```ts highlight={3}
+new SslMonitor("my-ssl-monitor", {
+ name: "Homepage Certificate",
+ frequency: Frequency.EVERY_1H,
+ /* More options ... */
+})
+```
+
+**Available frequencies**: `EVERY_1M`, `EVERY_2M`, `EVERY_5M`, `EVERY_10M`, `EVERY_15M`, `EVERY_30M`, `EVERY_1H`, `EVERY_2H`, `EVERY_3H`, `EVERY_6H`, `EVERY_12H`, `EVERY_24H`. SSL monitors do not support sub-minute frequencies (`EVERY_10S` / `EVERY_20S` / `EVERY_30S`).
+
+
+
+Array of [public location codes](/concepts/locations/#public-locations) where the monitor should run from. Multiple locations provide geographic coverage.
+
+```ts highlight={3}
+new SslMonitor("global-ssl", {
+ name: "Global Certificate Monitor",
+ locations: ["us-east-1", "eu-west-1", "ap-southeast-1"],
+ /* More options ... */
+})
+```
+
+
+
+Whether the SSL Monitor is enabled and will run according to its schedule.
+
+```ts highlight={3}
+new SslMonitor("my-ssl-monitor", {
+ name: "Homepage Certificate",
+ activated: false, // Disabled monitor
+ /* More options ... */
+})
+```
+
+
+## Examples
+
+
+
+ ```ts
+ import { Frequency, SslMonitor } from "checkly/constructs"
+
+ new SslMonitor("homepage-ssl", {
+ name: "Homepage Certificate",
+ frequency: Frequency.EVERY_1H,
+ locations: ["us-east-1", "eu-west-1"],
+ request: {
+ hostname: "example.com",
+ sslConfig: {
+ alertDaysBeforeExpiry: 30,
+ },
+ },
+ })
+ ```
+
+
+
+ ```ts
+ import { Frequency, SslAssertionBuilder, SslMonitor, TlsVersion } from "checkly/constructs"
+
+ new SslMonitor("api-strict-tls", {
+ name: "API — Strict TLS Policy",
+ frequency: Frequency.EVERY_10M,
+ locations: ["us-east-1", "eu-central-1"],
+ request: {
+ hostname: "api.example.com",
+ sslConfig: {
+ alertDaysBeforeExpiry: 45,
+ securityBaseline: {
+ enabled: true,
+ minTLSVersion: { value: "TLS1.2", severity: "fail" },
+ recommendedTLSVersion: { value: "TLS1.3", severity: "degrade" },
+ },
+ },
+ assertions: [
+ SslAssertionBuilder.chainTrusted().equals(true),
+ SslAssertionBuilder.hostnameVerified().equals(true),
+ SslAssertionBuilder.tlsVersion().greaterThanOrEqual(TlsVersion.TLS1_2),
+ ],
+ },
+ })
+ ```
+
+
+
+ ```ts
+ import { Frequency, SslAssertionBuilder, SslMonitor } from "checkly/constructs"
+
+ new SslMonitor("internal-ssl", {
+ name: "Internal Service Certificate",
+ frequency: Frequency.EVERY_6H,
+ privateLocations: ["my-private-location"],
+ request: {
+ hostname: "internal.corp.example.com",
+ port: 8443,
+ sslConfig: {
+ skipChainValidation: true,
+ alertDaysBeforeExpiry: 14,
+ },
+ assertions: [
+ SslAssertionBuilder.certNotExpired().equals(true),
+ SslAssertionBuilder.certExpiresInDays().greaterThan(7),
+ ],
+ },
+ })
+ ```
+
+
+
+ ```ts
+ import { Frequency, SslAssertionBuilder, SslMonitor } from "checkly/constructs"
+
+ new SslMonitor("mtls-endpoint", {
+ name: "mTLS API Gateway",
+ frequency: Frequency.EVERY_5M,
+ request: {
+ hostname: "mtls.api.example.com",
+ sslConfig: {
+ clientCertificateMode: "explicit",
+ sslClientCertificateId: "cert_abc123",
+ alertDaysBeforeExpiry: 30,
+ },
+ assertions: [
+ SslAssertionBuilder.certNotExpired().equals(true),
+ SslAssertionBuilder.hostnameVerified().equals(true),
+ ],
+ },
+ })
+ ```
+
+
+
+ ```ts
+ import { Frequency, SslMonitor } from "checkly/constructs"
+
+ // Monitor a certificate served for a specific SNI name
+ // on a shared IP (e.g. a CDN or multi-tenant server).
+ new SslMonitor("tenant-ssl", {
+ name: "Tenant Certificate via SNI",
+ frequency: Frequency.EVERY_1H,
+ request: {
+ hostname: "shared-cdn.example.net",
+ sslConfig: {
+ serverName: "tenant-a.example.com",
+ alertDaysBeforeExpiry: 20,
+ },
+ },
+ })
+ ```
+
+
diff --git a/detect/uptime-monitoring/ssl-monitors/configuration.mdx b/detect/uptime-monitoring/ssl-monitors/configuration.mdx
new file mode 100644
index 00000000..12a91566
--- /dev/null
+++ b/detect/uptime-monitoring/ssl-monitors/configuration.mdx
@@ -0,0 +1,113 @@
+---
+title: 'SSL Monitor Configuration'
+description: 'Configure your SSL monitor to track certificate health, expiry, and TLS security posture.'
+sidebarTitle: 'Configuration'
+---
+
+
+To configure an SSL monitor using code, learn more about the [SSL Monitor Construct](/constructs/ssl-monitor).
+
+
+### Basic Setup
+
+Configure your SSL monitor by specifying the target host:
+
+* **Hostname:** The domain or IP address to connect to (e.g. `api.example.com`). Do not include a scheme or port.
+* **Port:** The TCP port to connect to. Defaults to `443`.
+* **IP family:** Choose between IPv4 (default) or IPv6.
+
+### SSL Configuration
+
+Fine-grained control over the TLS handshake behavior:
+
+* **SNI server name (`serverName`):** Override the Server Name Indication sent during the TLS handshake. Useful when a single IP serves multiple certificates and you want to request a specific one. Defaults to the configured hostname when unset.
+
+* **Handshake timeout:** Maximum time in milliseconds to wait for the TLS handshake to complete. Range: 1,000–30,000 ms. Default: 10,000 ms.
+
+* **Alert days before expiry (`alertDaysBeforeExpiry`):** Raise a degraded alert when the certificate is within this many days of expiry. Range: 1–365 days. Default: 20 days. The monitor always fails immediately if the certificate is already expired.
+
+* **Skip chain validation:** When enabled, Checkly does not verify the certificate chain against trusted roots. The certificate is still inspected for expiry and the security baseline. Use this when monitoring internal or self-signed certificates.
+
+### Client Certificate / mTLS
+
+SSL monitors can present a client certificate during the TLS handshake to verify mTLS-protected endpoints:
+
+* **`clientCertificateMode: 'auto'`** — Checkly automatically selects a stored client certificate for the target host.
+* **`clientCertificateMode: 'explicit'`** — Use the specific certificate identified by `sslClientCertificateId`.
+
+Omit `clientCertificateMode` to connect without a client certificate (the default).
+
+Client certificates are stored at the account level under **Settings → Client Certificates**.
+
+### Security Baseline
+
+The security baseline is a configurable rule set that evaluates the negotiated TLS session against best-practice requirements. It runs on each successful handshake and certificate verification, and produces a **verdict** (`pass` / `warn` / `fail`) and a **grade** (`A-`, `B`, or `F`). It is skipped when an earlier step fails (e.g. hostname mismatch or chain untrusted).
+
+You can override the default severity for each rule:
+
+| Rule | Default severity | Configurable |
+|------|-----------------|:---:|
+| `minTLSVersion` | `fail` | ✅ |
+| `minKeySizeBits` | `fail` | ✅ |
+| `weakSignatureAlgorithm` | `fail` | ✅ |
+| `weakCipherSuite` | `fail` | ✅ |
+| `knownBadCA` | `fail` | ✅ |
+| `recommendedTLSVersion` | `ignore` | ✅ |
+| `recommendedKeySizeBits` | `ignore` | ✅ |
+| `ocspMustStapleRespected` | `ignore` | ✅ |
+| `sctPresent` | `ignore` | ✅ |
+
+Each rule accepts a `severity` of `fail`, `degrade`, or `ignore`. Set `enabled: false` to disable the baseline entirely for a monitor.
+
+
+Omitting the `securityBaseline` field inherits the account-level default baseline. Override it on a per-monitor basis only when you need non-standard thresholds for a specific endpoint.
+
+
+### Assertions
+
+Use assertions to validate specific certificate and TLS handshake properties beyond what the security baseline covers.
+
+Available assertion sources:
+
+| Source | Type | Description |
+|--------|------|-------------|
+| `CERT_NOT_EXPIRED` | boolean | Certificate has not passed its `notAfter` date |
+| `CERT_EXPIRES_IN_DAYS` | number | Days until the certificate expires |
+| `HOSTNAME_VERIFIED` | boolean | The certificate's SANs cover the monitored hostname |
+| `CHAIN_TRUSTED` | boolean | The full certificate chain is trusted by the system root store |
+| `TLS_VERSION` | string | Negotiated TLS protocol version (e.g. `TLS1.2`, `TLS1.3`) |
+| `CIPHER_SUITE` | string | IANA cipher suite name negotiated during the handshake |
+| `SIGNATURE_ALGORITHM` | string | Leaf certificate signature algorithm (e.g. `SHA256-RSA`, `ECDSA-SHA256`) |
+| `KEY_SIZE_BITS` | number | Leaf certificate public key size in bits |
+| `ISSUER_CN` | string | Common name of the certificate issuer |
+| `CERT_FINGERPRINT_SHA256` | string | SHA-256 fingerprint of the leaf certificate |
+| `ISSUER_FINGERPRINT_SHA256` | string | SHA-256 fingerprint of the issuer certificate |
+| `SAN_CONTAINS` | string | True when any Subject Alternative Name matches the given value |
+| `OCSP_STAPLED` | boolean | A stapled OCSP response was included in the handshake |
+| `HANDSHAKE_TIME_MS` | number | TLS handshake duration in milliseconds |
+
+For more details, see our documentation on [Assertions](/detect/assertions).
+
+### Response Time Limits
+
+Set performance thresholds based on the TLS handshake duration:
+
+* **Degraded After:** Handshake time (in milliseconds) at or above which the monitor is marked as degraded but not failed. Range: 0–30,000 ms. Default: 3,000 ms.
+* **Failed After:** Handshake time at or above which the monitor fails. Must be greater than or equal to the degraded threshold. Range: 0–30,000 ms. Default: 10,000 ms.
+
+### Frequency
+
+Set how often the monitor runs. SSL monitors run at most **once per minute** (every 1 minute to 24 hours) — sub-minute frequencies aren't available, since certificate state changes slowly.
+
+### Scheduling & Locations
+
+* **Strategy:** Choose between round-robin or parallel execution. Learn more about [scheduling strategies](/concepts/scheduling).
+* **Locations:** Select [public](/concepts/locations/#public-locations) or [private](/platform/private-locations/overview) locations to run the monitor from.
+
+### Additional Settings
+
+* **Name:** Give your monitor a clear name to identify it in dashboards and alerts.
+* **Description:** Add context about what this monitor does and why it matters. Supports markdown, max 500 characters. When a failure occurs, [Rocky AI](/ai/rocky-ai) uses the description to provide more accurate [root cause and user impact analysis](/resolve/ai-root-cause-analysis/overview).
+* **Tags:** Use tags to organize monitors across [dashboards](/communicate/dashboards/overview/) and [maintenance windows](/communicate/maintenance-windows/overview).
+* **Retries:** Define how failed runs should be retried. See [retry strategies](/communicate/alerts/retries).
+* **Alerting:** Configure your [alert settings](/communicate/alerts/configuration), [alert channels](/communicate/alerts/channels), or set up [webhooks](/integrations/alerts/webhooks) for custom integrations.
diff --git a/detect/uptime-monitoring/ssl-monitors/overview.mdx b/detect/uptime-monitoring/ssl-monitors/overview.mdx
new file mode 100644
index 00000000..13ef331e
--- /dev/null
+++ b/detect/uptime-monitoring/ssl-monitors/overview.mdx
@@ -0,0 +1,94 @@
+---
+title: 'SSL Monitor Overview'
+description: 'Monitor TLS certificate health, expiry, and security posture of your HTTPS endpoints.'
+sidebarTitle: Overview
+---
+
+
+**Monitoring as Code**: Learn more about the [SSL Monitor Construct](/constructs/ssl-monitor).
+
+
+## What are SSL Monitors?
+
+SSL monitors verify the health of a TLS certificate by connecting to a hostname, completing a full TLS handshake, and inspecting the resulting certificate and negotiated protocol parameters. Typical use cases include:
+
+* Detecting certificate expiry before it causes user-facing errors
+* Verifying that a recently-renewed certificate is deployed to all your servers
+* Enforcing minimum TLS version and cipher strength across your infrastructure
+* Catching chain trust failures caused by missing intermediate certificates
+* Alerting on weak or compromised certificate authority usage
+
+## How do SSL Monitors work?
+
+Each SSL monitor run performs the following steps:
+
+1. **DNS resolution** — If a hostname is provided, Checkly resolves it to an IP address
+2. **TCP connect** — A TCP connection is opened to the target host and port (default: 443)
+3. **TLS handshake** — A full TLS handshake is performed; an optional SNI server name override can be sent during this step
+4. **Certificate inspection** — The leaf certificate and chain are examined: expiry date, hostname verification, chain trust, signature algorithm, key size, SANs, and fingerprints are all captured
+5. **Security baseline evaluation** — On a successful handshake and certificate verification, a configurable rule set evaluates the negotiated protocol (TLS version, cipher suite, CA trust) and produces a per-rule verdict plus an overall **grade** and **verdict** (`pass` / `warn` / `fail`). The baseline is skipped when an earlier step fails (e.g. hostname mismatch or chain untrusted).
+6. **Assertions** — Optional assertions are evaluated against the certificate and handshake data
+
+## SSL Monitor Results
+
+Select a specific check run to inspect its results:
+
+* **Summary:** Shows the target hostname and port, the monitor state (passed, degraded, or failed), and the TLS handshake time
+
+* **Error details:** If the run failed, the error category and message explain what went wrong. Categories map to the stage where the failure occurred: `dns` (name resolution), `connect` (TCP connection), `timeout` or `handshake` (TLS handshake), `hostname` (certificate does not cover the monitored hostname), or `chain` (certificate chain not trusted). An expired certificate surfaces as a failure via a negative `daysUntilExpiry` value rather than a separate category.
+
+* **Handshake data:** The negotiated TLS version, cipher suite, and handshake duration in milliseconds
+
+* **Certificate details:** Subject, issuer, serial number, validity window (not-before / not-after), days until expiry, signature algorithm, key algorithm and size, subject alternative names (SANs), and SHA-256 fingerprint
+
+* **Security baseline:** The per-rule verdict table and the aggregate grade (`A-`, `B`, or `F`) produced by the security baseline evaluation
+
+* **Certificate chain:** The intermediate and root certificates presented during the handshake, with their subjects, issuers, validity dates, and fingerprints
+
+Learn more in our documentation on [Results](/concepts/results).
+
+## The Security Baseline
+
+The security baseline is a built-in rule set that evaluates each successful handshake and certificate verification against current best practices without requiring explicit assertions. Rules cover:
+
+| Rule | Default severity | What it checks |
+|------|-----------------|----------------|
+| `minTLSVersion` | `fail` | Negotiated TLS version is at or above the required minimum |
+| `minKeySizeBits` | `fail` | RSA key size meets the required minimum |
+| `weakSignatureAlgorithm` | `fail` | No non-root certificate in the chain uses MD2-RSA, MD5-RSA, SHA1-RSA, DSA-SHA1, or ECDSA-SHA1 |
+| `weakCipherSuite` | `fail` | The negotiated cipher suite is not in the known-weak list |
+| `knownBadCA` | `fail` | No certificate in the chain was issued by a distrusted CA |
+| `recommendedTLSVersion` | `ignore` (advisory) | Negotiated version meets the recommended minimum |
+| `recommendedKeySizeBits` | `ignore` (advisory) | RSA key meets the advisory size recommendation |
+| `ocspMustStapleRespected` | `ignore` (advisory) | If the leaf declares OCSP must-staple, a stapled response was provided |
+| `sctPresent` | `ignore` (advisory) | A Signed Certificate Timestamp was observed in the handshake |
+
+The aggregate **grade** is computed from the worst rule outcome:
+
+| Grade | Meaning |
+|-------|---------|
+| `A-` | All rules pass (or baseline disabled) |
+| `B` | At least one advisory or degrade-severity rule violated |
+| `F` | At least one fail-severity rule violated |
+
+You can override the default severity for each rule or disable the baseline entirely in the [SSL Config](/detect/uptime-monitoring/ssl-monitors/configuration#security-baseline).
+
+## Troubleshooting Common Issues
+
+
+This can happen when a server hosts multiple certificates and the default certificate served to Checkly does not cover the monitored hostname. The runner reports this as failure category `hostname`. Use the **SNI server name** (`serverName`) option in the SSL config to explicitly request the certificate for your hostname.
+
+If the certificate is legitimately shared across hostnames (e.g. a wildcard `*.example.com` that covers `api.example.com`) and hostname verification still fails, confirm the SAN list on the certificate contains the monitored hostname.
+
+
+
+A chain trust failure (failure category `chain`) means one or more intermediate certificates are missing or the root is not trusted. Common causes:
+
+* The server is not sending the full intermediate chain — verify with `openssl s_client -connect hostname:443 -showcerts`
+* The certificate was issued by a CA whose root was recently distrusted (e.g. Entrust 2024)
+* Self-signed or internal CA certificates — enable **Skip chain validation** in the SSL config if you only want to monitor expiry and other properties without trust verification
+
+
+
+The `alertDaysBeforeExpiry` setting (default: 20 days) controls when a degraded alert is raised due to approaching expiry. If you use aggressive certificate rotation you may want to lower this value; if you need more lead time to trigger your renewal process, raise it. The monitor always fails immediately when the certificate is expired (`daysUntilExpiry < 0`), regardless of this setting.
+
diff --git a/docs.json b/docs.json
index abfd76d5..3b885c5e 100644
--- a/docs.json
+++ b/docs.json
@@ -167,6 +167,13 @@
"detect/uptime-monitoring/icmp-monitors/configuration"
]
},
+ {
+ "group": "SSL Monitors",
+ "pages": [
+ "detect/uptime-monitoring/ssl-monitors/overview",
+ "detect/uptime-monitoring/ssl-monitors/configuration"
+ ]
+ },
{
"group": "Heartbeat Monitors",
"pages": [
@@ -481,6 +488,7 @@
"constructs/dns-monitor",
"constructs/tcp-monitor",
"constructs/icmp-monitor",
+ "constructs/ssl-monitor",
"constructs/heartbeat-monitor",
"constructs/agentic-check",
"constructs/api-check",