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

# Verify requests from SRE Agent

> Check the SRE-Agent-Signature header on requests SRE Agent sends to your endpoints, find and rotate your organization's key, and copy ready-made verifiers.

export const Plan = ({tier}) => <Badge color="blue">{tier} plan</Badge>;

<Plan tier="Free" />

Every request SRE Agent sends to an endpoint you configured carries an `SRE-Agent-Signature` header: an HMAC-SHA256 made with a key that belongs to your organization alone. Your proxy or application can check it to know that a request came from your organization's configuration, not just from the platform's shared address.

This page says what the header is, which requests carry it, where to find your key, how to verify at each hop, how to rotate the key and how to test a verifier before any traffic arrives. The key is on the **Request Signing** tab of **Settings**, which organization admins can open.

## Why verify

The hosted platform reads your endpoints from one fixed address, **52.7.196.239**, shared by every organization. The address proves nothing about which organization a request is for, so it must never be the only control on an endpoint. Keep requiring the credentials the endpoint already requires.

The signature says that a request is for your organization. It never replaces your credentials. A request sent through a forwarder is made by the forwarder from your network, so it arrives from the forwarder's address and not the platform's. It carries the same header.

## What the header is

```text theme={null}
SRE-Agent-Signature: v=1,kid=k_0123456789abcdef,t=1791640800,n=AAECAwQFBgcICQoLDA0ODw,b=e3b0c442...b855,s=TsDE9NBF...NOUU
```

| Field | Meaning |
| - | - |
| `v` | Scheme version, `1`. A receiver refuses any other value. |
| `kid` | The key id: `k_` plus 16 lowercase hex characters. It is public and opaque. It is not your organization id. |
| `t` | Unix time in seconds when the request was signed. |
| `n` | Nonce: 16 random bytes, base64url without padding (22 characters), new for every attempt. |
| `b` | Lowercase hex SHA-256 of the exact body bytes sent. The empty body hashes to `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`. |
| `s` | Base64url without padding of HMAC-SHA256(key, canonical string), 43 characters. |

Fields are `name=value` pairs separated by `,` with no spaces. The header never carries the key. The key is the string `srsk_` followed by 43 characters. Use that whole string, as is, as the HMAC key: the bytes of its UTF-8 text, with no decoding step.

Every header whose name starts with `sre-agent-` is reserved for the platform. The platform removes any such header you configured on a data source or a synthetic check before it signs, and the forms refuse those names, so a copy of a signature can never travel from one organization's configuration to another's.

## Which requests carry it

The platform signs a request to an endpoint named by your configuration when it is not signed with AWS credentials, the host is not an AWS service API endpoint and the host is not one of the third-party services listed below. That covers:

* data source reads (Prometheus, Loki, Grafana, Tempo, Elasticsearch, Jaeger, Zipkin, and Datadog or New Relic when the URL is your own proxy) and the completion reads of the Explorer;
* synthetic HTTP checks from the central location, on every hop to the target's own host (a hop to another host after a redirect is not signed);
* outbound alerting webhooks and Grafana annotations, signed afresh on every retry;
* Kubernetes API calls and the connector's `http_health_check`;
* self-hosted AI endpoints (an Ollama, an OpenAI-compatible gateway or a Portkey of your own);
* self-hosted Jira (Data Center behind your own edge).

Remote synthetic locations (the regional probes) do not sign yet, and neither do self-hosted identity providers used for single sign-on or AI providers of the LangChain type.

### Your endpoints under AWS domains

These endpoints are yours even though they live under an AWS or AWS-related domain, so they are signed. Each pattern needs a label in front of it, on a label boundary.

* An Application Load Balancer or Classic Load Balancer, `*.<region>.elb.amazonaws.com`, and a Network Load Balancer, `*.elb.<region>.amazonaws.com`.
* An API Gateway stage, `*.execute-api.<region>.amazonaws.com` (and `.vpce.amazonaws.com`).
* An S3 website endpoint, `*.s3-website*.amazonaws.com`.
* An EC2 public name, `ec2-<address>.compute-1.amazonaws.com` and `ec2-<address>.<region>.compute.amazonaws.com`.
* CloudFront (`*.cloudfront.net`), a Lambda function URL (`*.lambda-url.<region>.on.aws`), App Runner (`*.awsapprunner.com`) and Amplify (`*.amplifyapp.com`), which are not AWS service API hosts.

These are where your own edge (a load balancer, a WAF, CloudFront) first sees the request, so a verifier there can check it. The China partition (`.amazonaws.com.cn`) follows the same patterns.

### What is never signed

* Requests that SRE Agent signs with AWS credentials, and requests to AWS service API hosts, for example `monitoring.<region>.amazonaws.com`, `aps-workspaces.<region>.amazonaws.com` and an EKS API server.
* TCP and DNS checks, which have no HTTP header to carry it.
* Requests to third-party services, matched by host or DNS suffix on a label boundary: `datadoghq.com`, `datadoghq.eu`, `ddog-gov.com`, `newrelic.com`, `grafana.net`, `elastic-cloud.com`, `cloud.es.io`, `found.io`, `atlassian.net`, `jira.com`, `zoho.com`, `zoho.eu`, `zoho.in`, `zoho.com.au`, `zohocloud.ca`, `github.com`, `githubusercontent.com`, `pagerduty.com`, `slack.com`, `telegram.org`, `pushover.net`, `openai.com`, `anthropic.com`, `openrouter.ai`, `portkey.ai`, `googleapis.com`, `ollama.com`, `okta.com`, `oktapreview.com`, `microsoftonline.com`, `accounts.google.com`, `auth0.com` and `onelogin.com`.

A vendor's API has no verifier you control, and the header would only tell the vendor which organization a request is for.

## Find your key

<Steps>
  <Step title="Open the tab">
    As an organization admin, open **Settings** and select the **Request Signing** tab. Your
    organization's first key already exists: SRE Agent creates it with a new organization, or with
    the first signed request of one that existed before request signing.
  </Step>

  <Step title="Read the key list">
    The **Keys** table lists each key id with its state: **Signing** (in use now), **Pending until**
    a time, **Verify-only until** a time, **Retired** or **Revoked**. Key ids are public. The keys
    are not.
  </Step>

  <Step title="Reveal the key">
    Select **Reveal key** on the key marked **Signing**. The key appears with a **Copy** button and
    is cleared from the page when you leave the tab. Revealing a key is recorded in the audit log.
  </Step>

  <Step title="Install it in your verifier">
    Add the key id and the key to the verifier you chose below.
  </Step>
</Steps>

The **Where requests are signed** table on the same tab lists the hosts your configuration sends requests to and what happens for each: signed, signed through one of your forwarders, not signed because it is AWS, not signed because it is a third-party service, or not signed yet. Put a verifier where a signed host's requests first arrive.

### Check a signature

The **Check a signature** form answers whether a request your server received is genuine. Enter the **Method**, the **Host header**, the **Request target (path and query, as received)**, the **SRE-Agent-Signature header** and, optionally, **When it arrived** as Unix seconds or an ISO 8601 time, then select **Check**. The answer is about your organization's keys only.

## Where to verify

Verify at the first hop that sees the request as the platform sent it: a CloudFront Function, Lambda\@Edge, a Cloudflare Worker, nginx with njs or your application. A proxy that rewrites the `Host` header or re-encodes the path or query must either forward the originals (for example in `X-Forwarded-Host`) or run behind the verifier.

An edge that cannot read the body can still check the signature over the request line. Compare the body to `b` where you can read it.

To refuse a replay, keep the nonces you saw for 300 seconds. A retry always carries a new nonce, so a legitimate retry is never refused for it.

## Roll it out

Log the verdict for a week before you refuse anything. During a deploy of the platform, the previous version keeps sending unsigned requests for a few minutes, and a key rotation changes the key id in use. Allow a clock skew of 300 seconds either way and run NTP.

If the platform cannot read your key for a moment, it sends the request unsigned rather than not sending it, so keep logging unsigned requests after you start refusing bad signatures.

## Rotate or revoke a key

<Steps>
  <Step title="Create the new key">
    On the **Request Signing** tab, choose a delay next to **Rotate: start signing with a new key
    after** (**Now**, **1 hour**, **24 hours**, **3 days** or **7 days**; 24 hours is the default)
    and select **Rotate key**. A new key is created and waits as **Pending** until the delay ends.
    With **Now** there is no pending period: the new key signs at once, and a verifier that refuses
    bad signatures refuses requests until you install it. Only one key can be pending at a time.
  </Step>

  <Step title="Install it">
    Select **Reveal key** on the new key (**Pending**, or **Signing** if you chose **Now**) and add
    its key id and key to your verifier, which then accepts both ids.
  </Step>

  <Step title="Let it take over">
    At the end of the delay, or earlier with **Start signing with the new key now**, the platform
    signs with the new key and the old id stops appearing in requests. **Cancel pending key**
    discards a pending key that has not signed anything.
  </Step>

  <Step title="Remove the old key">
    The old key stays valid for verification for seven days after that, shown as **Verify-only
    until**, then it is retired. Remove it from your verifier at any time after the new key signs.
  </Step>
</Steps>

If a key leaks, select **Revoke and replace now**. The platform switches within a minute on every task and the old key never verifies again, so your verifier refuses requests until you install the new key.

## The canonical string

The signature covers nine lines joined with a line feed (`\n`), with no trailing newline, in UTF-8:

```text theme={null}
sre-agent-request-v1
<kid>
<t>
<n>
<METHOD>
<host>
<path>
<query>
<b>
```

* `METHOD` is upper case.
* `host` is the `Host` the receiver sees, lower case, with `:port` only when the port is not the scheme's default (80 for http, 443 for https) and an IPv6 literal in brackets. A request relayed through a forwarder is signed with the authority as your configured URL writes it, so a URL that spells out `:443` or `:80` is signed, and arrives, with that port.
* `path` is the request target's path exactly as sent, still percent-encoded, `/` when empty.
* `query` is everything after the first `?` exactly as sent, without the `?`, empty when there is none. It is not sorted or re-encoded. Compare what you received: `$request_uri` in nginx, `req.originalUrl` in Express, `args` in a WAF log.
* `b` is the same value as the header's `b`.

The platform builds the final URL once and signs those exact bytes, so what you receive is what was signed. A request whose method, host, path or query holds a CR or LF is never signed.

## Test vectors

Use the key `srsk_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8` (the base64url of the bytes 0 to 31), the key id `k_0123456789abcdef`, `t=1791640800` (2026-10-10 14:00:00 UTC) and the nonce `AAECAwQFBgcICQoLDA0ODw` (the bytes 0 to 15). Check your verifier against both vectors before any traffic arrives. Changing any one of the method, host, path, query, body, `t`, `n` or `kid` must make it fail.

```json theme={null}
{
  "key": "srsk_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8",
  "kid": "k_0123456789abcdef",
  "t": 1791640800,
  "vectors": [
    {
      "method": "GET",
      "host": "metrics.example.com",
      "target": "/api/v1/query?query=up&time=1791640800",
      "body": "",
      "header": "v=1,kid=k_0123456789abcdef,t=1791640800,n=AAECAwQFBgcICQoLDA0ODw,b=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,s=TsDE9NBFTh9ura4IkxD-gbsIsCut3jnuFwSJ8WkNOUU"
    },
    {
      "method": "POST",
      "host": "hooks.example.com",
      "target": "/sre-agent/alerts",
      "body": "{\"title\":\"disk full\"}",
      "header": "v=1,kid=k_0123456789abcdef,t=1791640800,n=AAECAwQFBgcICQoLDA0ODw,b=09685eb9c86df7299c4afd343fd9c72526b5ba687bce89f5fb786cb4e5770b6d,s=w55dXwxb7qxn5dtqqhOHYIY702oUTFrvLPKC2psThKk"
    }
  ]
}
```

## Verify in code

SRE Agent's own tests run every snippet below against exactly these vectors.

### Node

Express or any framework that exposes the raw request target and body.

```js theme={null}
import crypto from "node:crypto";

// kid -> key, from Settings → Request signing. Keep the old kid during a rotation.
const KEYS = { k_0123456789abcdef: process.env.SRE_AGENT_SIGNING_KEY };
const WINDOW_SECONDS = 300;

export function verifySreAgent({ method, host, target, header, body, now = Date.now() / 1000 }) {
  if (typeof header !== "string") return { ok: false, reason: "missing" };
  const f = {};
  for (const part of header.split(",")) {
    const i = part.indexOf("=");
    if (i > 0) f[part.slice(0, i).trim()] = part.slice(i + 1).trim();
  }
  if (f.v !== "1") return { ok: false, reason: "unsupported_version" };
  const key = Object.hasOwn(KEYS, f.kid) ? KEYS[f.kid] : undefined;
  if (!key) return { ok: false, reason: "unknown_key" };
  if (!/^[0-9]{1,12}$/.test(f.t ?? "") || Math.abs(now - Number(f.t)) > WINDOW_SECONDS)
    return { ok: false, reason: "stale" };
  const q = target.indexOf("?");
  const path = q === -1 ? target : target.slice(0, q);
  const query = q === -1 ? "" : target.slice(q + 1);
  const canonical = ["sre-agent-request-v1", f.kid, f.t, f.n, method.toUpperCase(),
    host.toLowerCase(), path, query, f.b].join("\n");
  const expected = crypto.createHmac("sha256", key).update(canonical, "utf8").digest();
  const given = Buffer.from(f.s ?? "", "base64url");
  if (given.length !== expected.length || !crypto.timingSafeEqual(given, expected))
    return { ok: false, reason: "bad_signature" };
  if (body !== undefined && crypto.createHash("sha256").update(body).digest("hex") !== f.b)
    return { ok: false, reason: "body_mismatch" };
  return { ok: true, kid: f.kid, nonce: f.n };
}
// verifySreAgent({ method: req.method, host: req.headers.host, target: req.originalUrl,
//                  header: req.headers["sre-agent-signature"], body: rawBodyBuffer })
```

### Python

```python theme={null}
import base64, hashlib, hmac, re, time

KEYS = {"k_0123456789abcdef": "srsk_..."}  # kid -> key from Settings
WINDOW_SECONDS = 300


def verify_sre_agent(method, host, target, header, body=None, now=None):
    if not header:
        return False, "missing"
    f = {}
    for part in header.split(","):
        k, sep, v = part.partition("=")
        if sep:
            f[k.strip()] = v.strip()
    if f.get("v") != "1":
        return False, "unsupported_version"
    key = KEYS.get(f.get("kid", ""))
    if key is None:
        return False, "unknown_key"
    t = f.get("t", "")
    now = time.time() if now is None else now
    if not re.fullmatch(r"[0-9]{1,12}", t) or abs(now - int(t)) > WINDOW_SECONDS:
        return False, "stale"
    path, _, query = target.partition("?")
    canonical = "\n".join(["sre-agent-request-v1", f["kid"], t, f.get("n", ""), method.upper(),
                           host.lower(), path, query, f.get("b", "")])
    expected = hmac.new(key.encode(), canonical.encode(), hashlib.sha256).digest()
    s = f.get("s", "")
    try:
        given = base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
    except ValueError:
        return False, "bad_signature"
    if not hmac.compare_digest(given, expected):
        return False, "bad_signature"
    if body is not None and hashlib.sha256(body).hexdigest() != f.get("b"):
        return False, "body_mismatch"
    return True, f["kid"]
```

### Go

Standard library only.

```go theme={null}
package sreagent

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// WindowSeconds is how far the request time may be from now, either way.
const WindowSeconds = 300

var timePattern = regexp.MustCompile(`^[0-9]{1,12}$`)

// Verify checks an SRE-Agent-Signature header. keys maps a kid to its key, both from
// Settings, Request signing; keep the old kid during a rotation. target is the request
// target as received (path and query); pass a nil body when this hop cannot read it,
// and check the body digest where it can.
func Verify(keys map[string]string, method, host, target, header string, body []byte, now time.Time) (string, error) {
	if header == "" {
		return "", errors.New("missing")
	}
	f := map[string]string{}
	for _, part := range strings.Split(header, ",") {
		if k, v, ok := strings.Cut(part, "="); ok {
			f[strings.TrimSpace(k)] = strings.TrimSpace(v)
		}
	}
	if f["v"] != "1" {
		return "", errors.New("unsupported_version")
	}
	key, ok := keys[f["kid"]]
	if !ok {
		return "", errors.New("unknown_key")
	}
	if !timePattern.MatchString(f["t"]) {
		return "", errors.New("stale")
	}
	t, _ := strconv.ParseInt(f["t"], 10, 64)
	if d := now.Unix() - t; d > WindowSeconds || d < -WindowSeconds {
		return "", errors.New("stale")
	}
	path, query, _ := strings.Cut(target, "?")
	canonical := strings.Join([]string{"sre-agent-request-v1", f["kid"], f["t"], f["n"],
		strings.ToUpper(method), strings.ToLower(host), path, query, f["b"]}, "\n")
	mac := hmac.New(sha256.New, []byte(key))
	mac.Write([]byte(canonical))
	given, err := base64.RawURLEncoding.DecodeString(f["s"])
	if err != nil || !hmac.Equal(given, mac.Sum(nil)) {
		return "", errors.New("bad_signature")
	}
	if body != nil {
		sum := sha256.Sum256(body)
		if hex.EncodeToString(sum[:]) != f["b"] {
			return "", errors.New("body_mismatch")
		}
	}
	return f["kid"], nil
}
```

## Verify at the edge

### nginx with njs

Load the module with `js_import sreagent from conf.d/nginx-sre-agent.js;` and `js_set $sre_agent_verdict sreagent.verdict;`, and set `$sre_agent_keys` to `kid=key` pairs separated by spaces, from an included file. Log `$sre_agent_verdict` first. Refuse with `if ($sre_agent_verdict != ok) { return 403; }` once the log is clean.

```js theme={null}
import crypto from 'crypto';

const WINDOW_SECONDS = 300;

function keys(r) {
  const out = Object.create(null);
  for (const pair of (r.variables.sre_agent_keys || '').split(/\s+/)) {
    const i = pair.indexOf('=');
    if (i > 0) out[pair.slice(0, i)] = pair.slice(i + 1);
  }
  return out;
}

function equal(a, b) {
  if (a.length !== b.length) return false;
  let diff = 0;
  for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
  return diff === 0;
}

function verdict(r) {
  const header = r.headersIn['SRE-Agent-Signature'];
  if (!header) return 'missing';
  const f = {};
  header.split(',').forEach((part) => {
    const i = part.indexOf('=');
    if (i > 0) f[part.slice(0, i).trim()] = part.slice(i + 1).trim();
  });
  if (f.v !== '1') return 'unsupported_version';
  const key = keys(r)[f.kid];
  if (!key) return 'unknown_key';
  const now = Math.floor(Date.now() / 1000);
  if (!/^[0-9]{1,12}$/.test(f.t || '') || Math.abs(now - Number(f.t)) > WINDOW_SECONDS) return 'stale';
  const target = r.variables.request_uri;
  const q = target.indexOf('?');
  const path = q === -1 ? target : target.slice(0, q);
  const query = q === -1 ? '' : target.slice(q + 1);
  const canonical = ['sre-agent-request-v1', f.kid, f.t, f.n, r.method.toUpperCase(),
    (r.headersIn['Host'] || '').toLowerCase(), path, query, f.b].join('\n');
  const expected = crypto.createHmac('sha256', key).update(canonical).digest('base64url');
  return equal(expected, f.s || '') ? 'ok' : 'bad_signature';
}

export default { verdict };
```

## CloudFront Function

Runtime `cloudfront-js-2.0`, with a KeyValueStore associated to the function that holds `kid -> key`. The function adds its verdict as an `x-sre-agent-verdict` header for your origin to log and refuses nothing. A viewer-request event carries no body, so the function leaves `b` to the origin.

CloudFront hands the function a parsed query string rather than the one that was sent. Measured on a live distribution: the values arrive still percent-encoded (`%20`, `+`, `%2B` and `%C3%A9` are passed as sent), the values of a repeated name keep their order, and the order of different names is not kept (`a=1&b=2&c=3` reached the function as `b`, `a`, `c`). So the function verifies a request with no query or with one parameter name exactly, and answers `unverifiable_query`, never `bad_signature`, for a query with two or more names. Enforce in Lambda\@Edge (whose `request.querystring` is the raw string) or in your application when your requests carry such queries, and treat an `unverifiable_query` verdict as a request to check further in, not as a forgery.

```js theme={null}
import cf from 'cloudfront';
const crypto = require('crypto');

const kvs = cf.kvs();
const WINDOW_SECONDS = 300;

// cloudfront-js-2.0 refuses `for...of`, so every loop here uses an index.
function sign(key, f, request, query) {
  const canonical = ['sre-agent-request-v1', f.kid, f.t, f.n, request.method.toUpperCase(),
    request.headers.host.value.toLowerCase(), request.uri, query, f.b].join('\n');
  return crypto.createHmac('sha256', key).update(canonical).digest('base64url');
}

function equal(a, b) {
  if (a.length !== b.length) return false;
  let diff = 0;
  for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
  return diff === 0;
}

// CloudFront passes query values still percent-encoded, but it does not keep the order of
// different parameter names, so only a query with at most one name can be rebuilt exactly.
// Its values keep their order. An empty value may have been sent with or without "=".
function queries(querystring) {
  const names = Object.keys(querystring);
  if (names.length === 0) return [''];
  if (names.length > 1) return null;
  const entry = querystring[names[0]];
  const values = entry.multiValue ? entry.multiValue.map((m) => m.value) : [entry.value];
  const parts = [];
  for (let i = 0; i < values.length; i++) parts.push(names[0] + '=' + values[i]);
  return values.length === 1 && values[0] === '' ? [names[0] + '=', names[0]] : [parts.join('&')];
}

async function verify(request, nowSeconds) {
  const header = request.headers['sre-agent-signature'];
  if (!header) return 'missing';
  const f = {};
  const fields = header.value.split(',');
  for (let i = 0; i < fields.length; i++) {
    const at = fields[i].indexOf('=');
    if (at > 0) f[fields[i].slice(0, at).trim()] = fields[i].slice(at + 1).trim();
  }
  if (f.v !== '1') return 'unsupported_version';
  let key;
  try {
    key = await kvs.get(f.kid);
  } catch (e) {
    return 'unknown_key';
  }
  if (!/^[0-9]{1,12}$/.test(f.t || '') || Math.abs(nowSeconds - Number(f.t)) > WINDOW_SECONDS) return 'stale';
  const candidates = queries(request.querystring);
  if (candidates === null) return 'unverifiable_query';
  for (let i = 0; i < candidates.length; i++) {
    if (equal(sign(key, f, request, candidates[i]), f.s || '')) return 'ok';
  }
  return 'bad_signature';
}

async function handler(event) {
  const request = event.request;
  request.headers['x-sre-agent-verdict'] = { value: await verify(request, Math.floor(Date.now() / 1000)) };
  return request;
}
```

## Check a request from your logs

To check a request after it arrived, your logs must hold everything the signature covers: the `SRE-Agent-Signature` value verbatim, the method, the `Host`, the raw path and query, and the time it arrived. Paste those into **Check a signature** on the **Request Signing** tab, or run them through a verifier above.

| Source | Can it be checked? | What you must do |
| - | - | - |
| AWS WAF logs | Yes | Keep the header: no redacted field or logging filter may drop `sre-agent-signature`. |
| AWS WAF sampled requests | Only for a request without a query | Sampled requests carry no query string, so a request that had one cannot be checked from them. Enable WAF logging instead. |
| Application logs | Yes, when the record holds the fields | Log, in one record: the request time, the method, the `Host`, the raw path, the raw query string and the `SRE-Agent-Signature` value verbatim. |
| ALB access logs, CloudFront standard logs | No | They record no request headers. Use WAF logs or an application log. |
| CloudFront real-time logs | No | `cs-headers` is truncated to 800 bytes, which loses the signature behind larger headers. |

## Plain http

A signature on a plain-http request can be read on the path. It is bound to its host, path, query, method and body and to five minutes, and it carries no key, but use https wherever you can.

## Related

* [Connect your data](/guides/get-started/connect-your-data): the data sources whose reads are signed.
* [Monitor endpoints with synthetic checks](/guides/prevent/synthetic-checks): the checks that carry the header.
* [Manage people and roles](/guides/administer/organizations-and-roles): who can open Settings.


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