Skip to content

Runs, states and uptime

Every HTTP and MCP test runs on a schedule, and each run ends in one outcome. This page lists the scheduling limits, what each outcome means, how outcomes move a test between states, and how uptime is computed from them. An agent reads runs with the get_api_test_runs MCP tool and uptime with get_uptime; in the app, open the test from the API Tests page.

SettingMCP parameterApp labelLimitsEditor default
IntervalintervalSecondsCheck everyAt least 30 seconds60 seconds
TimeouttimeoutSecondsGive up after1 to 30 seconds10 seconds
RetriesretriesRetries per run0 to 50
Failure thresholdfailureThresholdDown after1 to 10 failed runs3

The timeout bounds each attempt, not the whole run. Because the timeout is at most 30 seconds and the interval at least 30, a timeout is never longer than the interval.

A run is up to 1 + retries attempts. The failure threshold is how many failed runs in a row turn the test DOWN, and how many rate-limited runs in a row turn it RATE_LIMITED.

Before a test is saved, it runs once. The trial run sends and checks exactly what the scheduled runs will, which is why it belongs here, but it has rules of its own: it decides whether the save happens at all, it is never retried, and it does not appear in the run history.

  • One attempt, no retries, whatever the test’s retry setting. The trial run is not written to the run history.
  • Saved only on a success. A trial run that fails, errors or is rate limited saves nothing, and there is no way to save anyway. This holds whether the test is created enabled or paused.
  • On update, the trial run happens only when the change affects what the test sends or checks: the type, the HTTP target (URL, method, headers, body, credential), the MCP target, the HTTP assertions, or the timeout. Changing only the name, interval, retries, failure threshold or the enabled flag saves without one. A failed trial run leaves the existing test unchanged.

Over MCP, a refused create_api_test or update_api_test fails with a sentence that says what failed, starting Not created: or Not updated: and ending The test was not saved; fix its configuration and call create_api_test again. or The existing test is unchanged; fix the configuration and call update_api_test again. A passing save returns the run as trialRun on the test.

In the app, the save button reads Running test… while the trial run is in flight. A refused run stays on screen in the editor, headed Trial run failed, Trial run was rate limited by the target or Trial run could not be executed, with its cause, latency, failed assertions, the request and response, and a note starting The test was not saved. or Your changes were not saved.

StatusApp labelCausesWhat it means
SUCCESSSuccessNoneEvery assertion passed.
FAILUREFailureASSERTION, DNS, TLS, CONNECT, TIMEOUT, BLOCKED_ADDRESSThe target is unhealthy: an assertion failed, or the target could not be reached in time. Counts toward DOWN and against uptime.
ERRORErrorCREDENTIAL_DECRYPT, UNEXPECTED, EXECUTOR_LOSTA problem on Fixter’s side; the test could not run. Not counted against uptime.
RATE_LIMITEDRate limitedRATE_LIMITEDThe target answered HTTP 429. Not counted against uptime.
MISSEDMissedNoneThe run could not start in time, so the target was not probed. The row reads Never ran. Not counted against uptime.

Only runs whose request reached the target, or tried to, count as executed runs: SUCCESS, FAILURE and RATE_LIMITED. ERROR and MISSED runs do not count.

Each failed or errored run carries one cause, shown in the app as:

CauseApp labelWhen
ASSERTIONAssertion failedThe target answered, and one or more assertions failed.
DNSDNS lookup failedThe hostname did not resolve.
TLSTLS handshake failedThe TLS handshake failed.
CONNECTCould not connectThe connection failed, or the exchange could not be completed.
TIMEOUTTimed outNo complete response within the timeout.
BLOCKED_ADDRESSPrivate address blockedThe target resolved to a private or internal address. See Addresses Fixter will not test.
CREDENTIAL_DECRYPTCredential unreadableThe stored credential could not be read on Fixter’s side.
UNEXPECTEDUnexpected errorThe credential the test points at no longer exists (credential deleted), or something else went wrong while running the test (unexpected error while running the test).
EXECUTOR_LOSTExecutor lostAppears only on older runs; no new run gets it.
RATE_LIMITEDRate limited by the targetThe target answered HTTP 429.

A credential’s OAuth2 token fetch that fails is a FAILURE, not an ERROR; its message starts oauth2 token fetch: .

StateApp labelWhat it means
UPUpPassing, or not yet failing enough runs in a row to be DOWN. A new enabled test starts here.
DOWNDown · <n> failsThe failure threshold of failed runs in a row was reached. The label carries the current streak, for example Down · 3 fails.
RATE_LIMITEDRate limitedThe failure threshold of rate-limited runs in a row was reached. The app shows it as Rate limited.
PAUSEDPausedNot running on its schedule; no new runs are recorded.

A test created disabled starts PAUSED. In the app, Pause test and Resume test on the test’s page switch it; disable_api_test and enable_api_test do the same over MCP. Resuming returns the test to UP with both streaks cleared.

A test keeps two streaks: failed runs in a row and rate-limited runs in a row. Each run moves them as follows. ERROR and MISSED runs move nothing, and pausing parks a test in PAUSED from any state until it is enabled again, which starts it at UP.

  • A SUCCESS clears both streaks and returns a DOWN or RATE_LIMITED test to UP.
  • A FAILURE adds to the failure streak and clears the rate-limit streak. When the failure streak reaches the failure threshold, an UP or RATE_LIMITED test turns DOWN.
  • A RATE_LIMITED run adds to the rate-limit streak. When it reaches the failure threshold, an UP or DOWN test turns RATE_LIMITED. With a threshold of 1, the first 429 turns the test Rate limited.
  • An ERROR or MISSED run changes neither streak, so it neither breaks a run of failures nor adds to one.
  • A PAUSED test does not change state.

Uptime over a window is:

uptime = SUCCESS / (SUCCESS + FAILURE)

ERROR, MISSED and RATE_LIMITED runs are left out of both terms, because they say nothing about whether your endpoint was up. A window with no SUCCESS or FAILURE run has no uptime (the app shows -), not zero. Latency percentiles also count SUCCESS and FAILURE runs only.

A downtime interval is an unbroken streak of FAILURE runs, from the first failing run’s scheduled time to the last one’s finish. Only SUCCESS and FAILURE runs shape it: an ERROR, MISSED or RATE_LIMITED run in the middle neither ends a downtime nor starts one, so a rate-limited episode is never downtime.

Ask your agent for a test’s uptime and it calls get_uptime, which covers the last 24 hours unless given a window. In the app, the test’s Uptime and latency card shows Uptime, p95 latency and Runs with a count per outcome, and the latency chart marks downtime as bands.

Fixter’s tests reach public addresses only:

  • Public DNS only. Hostnames resolve through public DNS resolvers, never through an internal resolver, a hosts file or search domains. An internal name therefore does not resolve and fails as an ordinary DNS failure.
  • No private or internal addresses. If any address a hostname resolves to is private, loopback, link-local, cloud metadata, shared address space, documentation, benchmarking, multicast or reserved, in IPv4 or IPv6, the connection is refused. An IP address written in the URL is judged the same way. The run fails with cause BLOCKED_ADDRESS and the message the URL points at a private or internal address, which the monitor does not probe.
  • Redirects and OAuth2 token URLs too. Every connection passes the same check, each redirect hop included, and so does a credential’s OAuth2 token URL. A blocked token URL fails the run as BLOCKED_ADDRESS with the message prefixed oauth2 token fetch: .
  • No allowlist. There is no per-account exception.

A BLOCKED_ADDRESS run is a FAILURE: it is retried like any failure and counts toward DOWN. A trial run refused this way says, over MCP, Not created: the URL points at a private or internal address, which the monitor does not probe. Use a URL that is reachable from the public internet.

A run retries on any failure: a failed assertion, a timeout, or a connect, DNS, TLS or blocked-address failure, up to retries more attempts. It stops at the first attempt that is not a failure. A 429 is never retried, so a rate-limited run is one attempt whatever the retry setting. The app shows Took <n> attempts on a run that needed more than one.

On a 429 Fixter reads the target’s Retry-After header, as seconds or as an HTTP date. A zero, negative, unreadable or missing value defers nothing, and the value is capped at one hour. The next run moves to the rate-limited run’s finish time plus that Retry-After, but only when that is later than the next normal run; a 30 second Retry-After on a five minute test defers nothing. Runs skipped by a deferral are not recorded as MISSED.

The run message says which happened:

  • Rate limited by the target (HTTP 429) when nothing was deferred.
  • Rate limited by the target (HTTP 429); next check deferred by <N> seconds, or ... by <N> minutes, when the deferral moved the next run. The duration is how far past the next normal run it moved, in seconds under 120 seconds and in whole minutes from there.

An HTTP test whose status pattern accepts 429 (for example 429 or 4xx) treats a 429 as an ordinary response, and none of this applies. See HTTP tests. For letting the tests through a rate limiter, see Allow tests through your firewall.

Every run gets one W3C trace id. Each attempt sends it to your endpoint in a traceparent header (same trace id across a run’s retries, new span id per attempt), and {{traceId}} is the same id. A service that propagates incoming trace context, which OpenTelemetry SDKs do by default, continues that trace instead of opening its own, so the spans and logs it records for the request carry Fixter’s trace id. If the service reports to Fixter, that is what links the run to them. To keep your own ids, set a traceparent request header on an HTTP test; a header you set yourself replaces Fixter’s.

get_api_test_runs returns each run’s outcome, timing, failed assertions, traceId and retryAfterSeconds; correlate on the traceId finds what happened inside your service. The request and response themselves are not returned over MCP; they are in the app.

Open a run on the test’s page:

  • The run message and the failed assertions.
  • The trace id, with Open in Logs (logs for that trace around the run’s time) and View trace.
  • Request: the method, URL, headers and body of the run’s last attempt. For an MCP test this is the last request of the session, not counting the closing session DELETE.
  • Response: the status, first byte and total time, headers and body, or The target never answered.

Authorization, Proxy-Authorization, Cookie, X-Api-Key and Api-Key are always shown as <redacted>, even when typed into the test’s own headers, and so is every header and query parameter a credential adds. The request body is shown as sent. Bodies are cut at 16 KB; a cut response body says Body cut at 16 KB. The target returned more.