Skip to content
Sigtake Academy Start free

How to Monitor an Authenticated API Health Endpoint

A 200 only proves something answered. This guide sends your token with every check, keeps it encrypted, and checks what the response actually says, so a health endpoint reporting a dead database pages your team even though the status code looks fine.

This builds on a basic monitor. If you don't have one yet, start with setting up website uptime monitoring: everything there, from the failure threshold to automatic recovery, works the same way here.

Why the status code isn't enough

Three things a status-code check misses:

  • The endpoint needs a token. Without one, a protected /health answers 401 and the monitor is down all the time, so it gets switched off and protects nothing.
  • The app is up and its dependencies aren't. Many health endpoints answer 200 with {"status": "degraded"} when the database or a queue is unreachable.
  • Something else answered. A load balancer or CDN serving a maintenance page returns 200 with HTML instead of your JSON.

Headers fix the first. Response assertions fix the other two: they read the JSON body and fail the check when a field doesn't say what it should. A body that isn't JSON at all fails every assertion, which is how the maintenance page gets caught.

A check sends the request with an encrypted Authorization header. The response must have the expected status code and every assertion must pass, for example status equals ok and services.db equals ok. Only then does the check pass. REQUEST RESPONSE RESULT GET /health Authorization 🔒 encrypted 200 status = "ok" services.db = "ok" Check passes Wrong status or any failed assertion fails the check A check sends the request with an encrypted Authorization header. The response must have the expected status code and every assertion must pass, for example status equals ok and services.db equals ok. Only then does the check pass. GET /health Authorization 🔒 encrypted 200 status = "ok" services.db = "ok" Check passes Any failed assertion fails the check
Status code first, then every assertion. All of them must pass.

Set it up

The examples use a health endpoint that returns this when everything is fine:

{
  "status": "ok",
  "version": "2.14.0",
  "checked_at": "2026-10-04T09:12:44Z",
  "services": { "db": "ok", "redis": "ok" }
}
  1. Open Advanced settings on the monitor

    Edit an existing monitor, or add a new one pointing at your health endpoint, and expand Advanced settings at the bottom of the form.

    Method stays GET for almost every health endpoint. POST, PUT and PATCH let you send a body, which goes out as JSON unless you set your own Content-Type header. HEAD skips the body entirely, so it can't be combined with assertions.

  2. Add the token as an encrypted header

    Under Request headers, use the preset that matches how your API authenticates: Bearer token, API key or Basic auth. Each one adds an encrypted header. Paste the value and leave the lock closed.

    • Bearer token adds Authorization already starting with Bearer . Paste the token after it; the value is masked, so use the eye icon to check the space is there.
    • API key adds X-Api-Key. Rename it if your API expects another header name.
    • Basic auth asks for a username and password and stores the encoded Authorization header for you.

    Give the monitor its own token with read-only access to the health endpoint, not a personal or admin key. If it ever needs revoking, nothing else breaks. Credentials written into the URL, like https://user:pass@…, are refused for the same reason: a URL gets shown and logged, a secret header doesn't.

  3. Paste a healthy response

    Under Response assertions, use Paste a healthy response and paste what the endpoint returns on a good day. Each field becomes an assertion.

    Fields that change on every call only get checked for presence: numbers, dates, IDs and lists. Everything else must match exactly. The example above becomes:

    FieldCheck
    $.statusequals ok
    $.versionequals 2.14.0
    $.checked_atexists
    $.services.dbequals ok
    $.services.redisequals ok
  4. Remove the assertions that would break on a normal day

    Delete anything that changes without anything being wrong, such as a version string that moves on every deploy. Keep the fields that say whether each dependency is healthy.

    Here that means deleting $.version: left in, your next release would page the team. Ask of each row "if this changed, would something be broken?" and keep only the yeses. Fields you don't list are ignored, so the endpoint can grow new ones without breaking the monitor.

  5. Save, then break an assertion on purpose

    Save the monitor and wait for a passing check. Then change one expected value to something the endpoint never returns, save, and wait for the Down alert. Put the value back and the alert resolves on the next passing check.

    For example, set $.services.db to equal ok-test. The monitor's recent checks show the reason in the Error column, Assertion failed: path '$.services.db' should equal "ok-test", and the alert carries the same text. A failed assertion is sent as a medium severity alert. A connection failure or wrong status code is high, since nothing answered at all. To see the alert sooner, use a 1 minute interval and a failure threshold of 1 while you test, then set them back.

Writing assertions

Each assertion has a path into the JSON, an operator and, for two of the operators, a value.

OperatorPasses when
equalsThe field is exactly the value: same type, same content.
containsText includes the value, a list has an item matching it, or an object has at least the fields you give.
existsThe field is present, even if it's null.
not emptyThe field is present and isn't null, "", [] or {}. 0 and false pass.
  • Paths start at $, which the form shows for you. Nest with dots and pick list items by position: services.db, checks[0].name. Field names can use letters, digits, _ and -.
  • Values are read as JSON. 200 is a number, true a boolean, ok plain text. If the endpoint returns the text "200", type it with quotes, or equals fails on the type.
  • The JSON editor (the Visual / JSON switch) takes the shape you expect as one object: { "status": "ok", "services": { "db": "ok" } } passes when the response contains at least those fields with those values. For any other check, give a list of rules such as [{ "path": "$.checked_at", "operator": "exists" }].
  • Up to 20 assertions per monitor, and the response must be under 1 MB.

How the token is kept

  • Encrypted at rest, and write-only. Once saved, a secret header is never shown again: not in the form, not in the API. You can replace it, not read it back. The form says "Saved encrypted · type to replace".
  • It doesn't follow redirects to other sites. If the endpoint redirects to a different origin, the check fails with "Redirect to a different origin blocked" instead of handing your token to someone else. Point the monitor at the final URL.
  • Changing the URL to another origin asks for it again. Move a monitor from api.example.com to api.other.com and saving asks you to re-enter each secret, so a stored token can't be pointed at a new host without someone typing it.
  • Alerts never quote the response. A failed assertion describes the rule, not the value received, because an endpoint can echo a request header back into its body.
  • Prefer plain-text headers for anything that isn't secret, such as Accept: they stay visible and editable. Click the lock to switch a header between plain and encrypted.

If a check fails unexpectedly

What you seeLikely cause
Credentials rejected by the targetThe token is wrong, expired or sent under the wrong header name. With Bearer, check the value still starts with Bearer followed by a space.
response body is not valid JSONSomething other than your API answered, such as a maintenance page or login screen, or the endpoint returns plain text. Check the URL, and that the token reached it.
An equals check fails while the response looks rightThe type differs: 200 versus "200", or true versus "true". Add or remove the quotes.
Alerts after every deployAn assertion checks something that changes on release, like a version or build number. Remove it, or use exists instead.
Redirect to a different origin blockedThe URL redirects to another domain or subdomain while carrying a secret. Use the final URL directly.
Saving asks to re-enter a secretYou changed the URL to a different origin. Type the value again, or put the old URL back.
"HEAD has no body" when savingAssertions need a body. Switch the method to GET.
Response body exceeds 1024 KBThe endpoint returns too much. Point the monitor at a smaller health route.

Catch the outage a 200 hides

Free during early access. Unlimited users, teams, alerts and monitors — no credit card.