# Synthetics

Synthetics allow you to proactively monitor the health, availability, and performance of your endpoints. By simulating requests from your infrastructure, you can verify that your critical services are reachable and returning correct data, even when no real user traffic is active.

### Overview

groundcover Synthetics execute checks from your installed groundcover backend, working on BYOC deployments only.

* **Source**: Checks run from within your backend, when having multiple groundcover backends you can select the specific backend to use. We will support region selection for running tests from specific locations.
* **Supported Protocols**: HTTP/HTTPS is fully supported from the UI. SSL/TLS, TCP, and DNS checks are supported via the [Terraform provider](https://registry.terraform.io/providers/groundcover-com/groundcover/latest/docs/resources/synthetic_test) while the UI is being rolled out. Additional protocols (gRPC, ICMP/Ping, UDP, WebSocket) are coming soon.
* **Alerting**: Creating a Synthetic test automatically creates a corresponding Monitor (See: [Monitors](/use-groundcover/monitors.md)). Using monitors you can get alerted on failed synthetic tests, see: [Notification Channels](/integrations/workflow-integrations.md) . The monitor is uneditable.
* **Trace Integration**: We generate traces for all synthetic tests, which you can see as first-class citizens in groundcover platform. You can query these traces by using `source:synthetics` in traces page.

### Creating a Synthetic Test

Navigate to Monitors > Synthetics and click `+ Create Synthetic Test` .

{% hint style="info" %}
Only Editors can create/edit/delete synthetic tests, see [Role-Based Access Control (RBAC)](/use-groundcover/role-based-access-control-rbac.md#default-policies)
{% endhint %}

#### Request Configuration

Define the endpoint and parameters for the test. The available settings depend on the selected synthetic type.

**Common settings (all types)**

* **Synthetic Test Name**: A descriptive name for the test.
* **Interval**: Frequency of the check (e.g., every 60s). Must be at least 15s and less than 1h. The interval must also be greater than the total worker timeout (including retries).
* **Custom Labels**: Add custom labels that will exist on traces generated by checks. You can use these labels to filter traces.

**HTTP settings**

* **Target**: Select the HTTP method and URL. Include HTTP scheme (`http://` or `https://`), for example: `https://api.groundcover.com/api/backends/list`. Supported methods: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`.
  * *Tip:* Use Import from cURL to paste a command and auto-fill these fields.
* **Follow redirects**: Whether the test should follow 3xx responses. Defaults to `true`. When disabled, the test returns the 3xx response as the result set for assertions.
* **Allow insecure**: Disables SSL/TLS certificate verification. Defaults to `false`. Use this only for internal testing or self-signed certificates — it exposes you to Man-in-the-Middle attacks on production endpoints.
* **HTTP Version**: The UI exposes `HTTP/1.0`, `HTTP/1.1` and `HTTP/2.0`; default is `HTTP/1.1`.
* **Timeout**: Max duration to wait before marking an attempt as failed. Required for HTTP checks (e.g., `30s`, `60s`). **The interval must be greater than the total worker timeout** (timeout × retry count + retry intervals).
* **Payload**: Set the body type and content if your request requires data (e.g., POST/PUT). Supported body types: `json`, `text`, `raw`. Omit the body entirely to send no payload.
* **Headers**: Custom headers as key/value pairs.
* **Authentication**: Supported types: `none` (default), `basic` (requires username and password), `bearer` (requires a token).

**SSL settings**

* **Host**: Hostname to connect to (e.g., `example.com`).
* **Port**: Port to connect to (typically `443`). Must be between 1 and 65535.
* **Verify**: Verify the certificate chain. Set to `false` to accept self-signed or invalid certificates.
* **Min Version**: Minimum accepted TLS version (e.g., `1.2`, `1.3`).
* **SNI**: Server Name Indication override.
* **Timeout**: Max duration for the TLS handshake. Default is 10s.

**TCP settings**

* **Host**: Hostname or IP to connect to.
* **Port**: TCP port to connect to. Must be between 1 and 65535.
* **Send**: Optional string payload to send after connecting.
* **Expect Response**: Whether to read a response back.
* **Receive Max Bytes**: Maximum bytes to read from the response.
* **Timeout**: Max duration for the connection and read. Default is 10s.

**DNS settings**

* **Domain**: Domain name to query (e.g., `example.com`).
* **Record Type**: One of `A`, `AAAA`, `CNAME`, `TXT`, `MX`, `NS`, `SOA`, `PTR`.
* **Resolver**: Optional DNS resolver to use. Defaults to the system resolver.
* **Port**: Optional DNS port. Must be between 1 and 65535 when set.
* **DNSSEC**: Whether to enable DNSSEC validation.
* **Timeout**: Max duration to wait for the DNS response. Default is 10s.

#### Assertions (Validation Logic)

Assertions are the rules that determine the outcome of a check. You can add multiple assertions to a single test. Each assertion evaluates independently to either **pass** or **fail**. The overall check outcome depends on the `severity` of the assertions that evaluate to fail:

* If any assertion with `severity: critical` evaluates to fail, the check outcome is **failed**. This triggers the auto-generated monitor alert.
* If no critical assertions fail but any assertion with `severity: degraded` evaluates to fail, the check outcome is **degraded**. This does *not* trigger the auto-generated monitor alert.
* If all assertions pass, the check outcome is **passed**.

An assertion has the following fields:

* **`source`** — which part of the response to inspect (e.g., `statusCode`, `jsonBody`, `certificateValid`, `tcpConnection`).
* **`property`** — required for `responseHeader` (header name) and `jsonBody` (dot-notation JSON path). Not used by other sources.
* **`operator`** — how to compare the `source` value against the `target` (e.g., `eq`, `contains`, `gt`).
* **`target`** — the expected value. Required for most operators except `exists`/`notExists`.
* **`severity`** *(optional)* — `critical` (default) or `degraded`. Controls how a failed assertion affects the check outcome: `critical` failures fail the check; `degraded` failures mark it as degraded without failing it.

**Assertion Sources per Synthetic Type**

The `source` field must be compatible with the synthetic type. The table below lists every valid source, the synthetic types it applies to, and the operators the worker currently validates.

| Source                 | Applies To          | Property                                                  | Operators                                                                                     | Target                                                                                                                                                                      |
| ---------------------- | ------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `statusCode`           | HTTP                | *not used*                                                | `eq`, `ne`, `gt`, `lt`                                                                        | HTTP status code as a numeric string (e.g. `"200"`, `"404"`)                                                                                                                |
| `responseHeader`       | HTTP                | Header name (case-insensitive), e.g. `Content-Type`       | `exists`, `notExists`, `eq`, `ne`, `contains`                                                 | Expected header value (required for `eq`/`ne`/`contains`)                                                                                                                   |
| `responseBody`         | HTTP                | *not used*                                                | `contains`, `eq`, `ne`, `exists`, `notExists`                                                 | Substring or full body to match against. `exists`/`notExists` check whether the body is non-empty.                                                                          |
| `jsonBody`             | HTTP                | Dot-notation JSON path (e.g. `data.user.name`). Required. | `exists`, `notExists`, `eq`, `ne`, `contains`                                                 | Expected value at the JSON path. For `eq`, values that look like JSON (`{…}`, `[…]`, `"..."`), numbers, or booleans are auto-parsed; anything else is compared as a string. |
| `responseTime`         | HTTP, SSL, TCP, DNS | *not used*                                                | `gt`, `lt`                                                                                    | Threshold in milliseconds as a string (e.g. `"5000"`)                                                                                                                       |
| `certificateValid`     | SSL                 | *not used*                                                | `eq`                                                                                          | `"true"` or `"false"`                                                                                                                                                       |
| `certificateExpiresIn` | SSL                 | *not used*                                                | `gt`, `lt`                                                                                    | Days until expiry as a string (e.g. `"30"`)                                                                                                                                 |
| `tlsVersion`           | SSL                 | *not used*                                                | `eq`, `gt`, `lt`                                                                              | TLS version string (e.g. `"1.2"`, `"1.3"`)                                                                                                                                  |
| `chainValid`           | SSL                 | *not used*                                                | `eq`                                                                                          | `"true"` or `"false"`                                                                                                                                                       |
| `cipherSuite`          | SSL                 | *not used*                                                | `eq`, `contains`                                                                              | Cipher name (e.g. `"AES256"`)                                                                                                                                               |
| `tcpConnection`        | TCP                 | *not used*                                                | `exists`, `notExists`, `eq`, `ne`                                                             | `"true"`/`"false"` for `eq`/`ne`                                                                                                                                            |
| `responseContains`     | TCP                 | *not used*                                                | `contains`                                                                                    | Substring to match in received payload (requires `send` + `expectResponse` in the TCP request)                                                                              |
| `dnsAnswer`            | DNS                 | *not used*                                                | `exists`/`notExists` (does DNS resolve at all), `eq`, `ne`, `contains` (match a record value) | Expected DNS record value (e.g. an IP for A records). Not used for `exists`/`notExists`.                                                                                    |

{% hint style="warning" %}
The `target` field is always a **JSON string**, even when the value represents a number or boolean. The API rejects native JSON numbers and booleans (e.g., `"target": 200` returns 400). Use quoted values: `"target": "200"`, `"target": "true"`. The worker coerces numeric and boolean strings automatically for sources that require them (`statusCode`, `responseTime`, `certificateExpiresIn`, `tlsVersion`, `certificateValid`, `chainValid`). For `jsonBody` with operator `eq`, string targets that look like JSON, numbers, or booleans (`"true"`, `"false"`, `"123"`, `"{...}"`) are auto-parsed before comparison.
{% endhint %}

{% hint style="info" %}
The API schema also accepts the operators `startsWith`, `endsWith`, `regex`, and `oneOf`, and the sources `udp` and `websocket`. These are reserved for future use — the worker does not currently validate them and assertions using them will fail with an "Unsupported operator" or "Unknown assertion source" result.
{% endhint %}

**Assertion Operators**

The `operator` defines the logic applied to the source/property.

| Operator    | Function                                                                                                                                             | Example                                  |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| `eq`        | Equals. Numeric compare for `statusCode`, `responseTime`, `tlsVersion`, `certificateExpiresIn`; otherwise string equality (case-sensitive).          | `statusCode` `eq` `200`                  |
| `ne`        | Not equal.                                                                                                                                           | `statusCode` `ne` `500`                  |
| `gt`        | Numeric greater-than. Used with `statusCode`, `responseTime`, `certificateExpiresIn`, `tlsVersion`.                                                  | `responseTime` `gt` `100`                |
| `lt`        | Numeric less-than. Same sources as `gt`.                                                                                                             | `responseTime` `lt` `5000`               |
| `contains`  | Checks if a substring exists within the source value.                                                                                                | `responseBody` `contains` `"error"`      |
| `exists`    | Checks that the field/resource is present. For `dnsAnswer`, checks that DNS resolves. For `tcpConnection`, checks that the TCP connection succeeded. | `responseHeader` (`set-cookie`) `exists` |
| `notExists` | Opposite of `exists`.                                                                                                                                | `jsonBody` (`error_message`) `notExists` |

### Examples

All examples below represent the JSON payload accepted by the `POST /api/synthetics/v1/rules` endpoint. HTTP checks can also be created via the UI, which renders this structure as form fields. SSL/TLS, TCP, and DNS checks are currently supported via the [Terraform provider](https://registry.terraform.io/providers/groundcover-com/groundcover/latest/docs/resources/synthetic_test) while UI support is being rolled out. For Terraform HCL examples, see the [provider documentation](https://registry.terraform.io/providers/groundcover-com/groundcover/latest/docs/resources/synthetic_test).

#### HTTP — health check with multiple assertions

Validates that an API endpoint returns `200`, completes within 5 seconds, exposes a `Content-Type` header, and that the JSON body's `status` field equals `"ok"`.

```json
{
  "version": 1,
  "name": "Homepage Health Check",
  "interval": "1m",
  "checkConfig": {
    "kind": "http",
    "metadata": { "syntheticName": "Homepage Health Check" },
    "request": {
      "http": {
        "kind": "http",
        "url": "https://example.com/api/health",
        "method": "GET",
        "timeout": "30s",
        "followRedirects": true
      }
    },
    "executionPolicy": {
      "assertions": [
        { "source": "statusCode",     "operator": "eq",     "target": "200" },
        { "source": "responseTime",   "operator": "lt",     "target": "5000" },
        { "source": "responseHeader", "operator": "exists", "property": "Content-Type" },
        { "source": "jsonBody",       "operator": "eq",     "property": "status", "target": "ok" }
      ]
    }
  }
}
```

#### SSL — certificate validity and expiration

Checks that the certificate served by `example.com:443` is valid, its chain is valid, and it expires in more than 30 days.

```json
{
  "version": 1,
  "name": "SSL Certificate Check",
  "interval": "15m",
  "checkConfig": {
    "kind": "ssl",
    "metadata": { "syntheticName": "SSL Certificate Check" },
    "request": {
      "ssl": {
        "kind": "ssl",
        "host": "example.com",
        "port": 443,
        "verify": true,
        "minVersion": "1.2"
      }
    },
    "executionPolicy": {
      "assertions": [
        { "source": "certificateValid",     "operator": "eq", "target": "true" },
        { "source": "chainValid",           "operator": "eq", "target": "true" },
        { "source": "certificateExpiresIn", "operator": "gt", "target": "30"   }
      ]
    }
  }
}
```

#### TCP — connectivity and response

Opens a TCP connection to `db.example.com:5432`, asserts the connection succeeded, and asserts the handshake completed in under 1 second.

```json
{
  "version": 1,
  "name": "Database TCP Check",
  "interval": "30s",
  "checkConfig": {
    "kind": "tcp",
    "metadata": { "syntheticName": "Database TCP Check" },
    "request": {
      "tcp": {
        "kind": "tcp",
        "host": "db.example.com",
        "port": 5432,
        "timeout": "5s"
      }
    },
    "executionPolicy": {
      "assertions": [
        { "source": "tcpConnection", "operator": "exists", "target": "true" },
        { "source": "responseTime", "operator": "lt", "target": "1000" }
      ]
    }
  }
}
```

#### DNS — A record resolution

Queries `example.com` for its `A` record and asserts the answer contains the expected IP.

```json
{
  "version": 1,
  "name": "DNS A Record Check",
  "interval": "5m",
  "checkConfig": {
    "kind": "dns",
    "metadata": { "syntheticName": "DNS A Record Check" },
    "request": {
      "dns": {
        "kind": "dns",
        "domain": "example.com",
        "recordType": "A"
      }
    },
    "executionPolicy": {
      "assertions": [
        { "source": "dnsAnswer",    "operator": "contains", "target": "93.184.216.34" },
        { "source": "responseTime", "operator": "lt",       "target": "2000" }
      ]
    }
  }
}
```

#### Assertion severity and retries

Assertions with `severity: critical` (the default) that evaluate to fail mark the check as **failed**. Assertions with `severity: degraded` that evaluate to fail mark the check as **degraded** — a warning state that does not count as a failure. Use `retries` to re-attempt the check before settling on the final outcome, avoiding false alerts on transient failures.

In the example below, if the status code assertion (`severity: critical`) evaluates to fail the check is failed; if only the response time assertion (`severity: degraded`) evaluates to fail the check is degraded but not failed.

```json
{
  "version": 1,
  "name": "Performance Check",
  "interval": "30s",
  "checkConfig": {
    "kind": "http",
    "metadata": { "syntheticName": "Performance Check" },
    "request": {
      "http": {
        "kind": "http",
        "url": "https://api.example.com/status",
        "method": "GET",
        "timeout": "5s"
      }
    },
    "executionPolicy": {
      "retries": { "count": 3, "interval": "500ms" },
      "assertions": [
        { "source": "statusCode",   "operator": "eq", "target": "200", "severity": "critical" },
        { "source": "responseTime", "operator": "lt", "target": "300", "severity": "degraded" }
      ]
    }
  }
}
```

### Auto-Generated Monitors

When you create a Synthetic Test, groundcover eliminates the need to manually configure separate alert rules. A Monitor is automatically generated and permanently bound to your test. See: [Monitors](/use-groundcover/monitors.md) .

* **Managed Logic**: The monitor queries for checks with outcome `failed` (i.e., a `severity: critical` assertion evaluated to fail). Checks with outcome `degraded` (only `severity: degraded` assertions failed) do **not** trigger the monitor.
* **Lifecycle**: The monitor transitions between `Pending`, `Firing` (when the test outcome is `failed`), and `Resolved` (when the test passes or is only degraded).
* **Zero Maintenance**: You do not need to edit this monitor's query. Any changes you make to the Synthetic Test (such as changing the target URL or assertions) are automatically synced to the Monitor.

> Note: To prevent configuration drift, these auto-generated monitors are read-only. You cannot edit their query logic directly; you simply edit the Synthetic Test itself.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.groundcover.com/capabilities/synthetics.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
