ScoutSentinelScoutSentinel
Menu

API overview

Authentication, scopes, conventions, errors, pagination, filters and the resource map for the ScoutSentinel REST API.

v1Updated

The API lives at https://api.scoutsentinel.com with every resource under /v1. It is what the product application itself uses, so anything you can do in the app has an endpoint; a subset of routes is also open to API keys, and those are the ones documented here.

Authentication

Two credentials are accepted on the public API.

API keys. Create one under Settings, API keys (POST /v1/api-keys, admin role). A key has a name, one or more scopes, and an optional expiresAt. Keys currently grant those permissions across the organisation, not one environment. Creating an environment-restricted key is not supported: a supplied environmentId is rejected rather than silently ignored. Existing keys have not gained an environment restriction. The secret is shown once; afterwards only the visible prefix (for example ss_live_3f9a) is listed. Send it as a bearer token:

GET /v1/journeys?status=healthy,degraded&range=7d HTTP/1.1
Host: api.scoutsentinel.com
Authorization: Bearer ss_live_<prefix>_<secret>

Keys are hashed at rest and cannot be recovered; revoke with DELETE /v1/api-keys/{apiKeyId} and create a replacement. There is no overlap period built into the API; run two keys side by side while you roll over.

Session cookie. The product application signs in through /v1/auth/* and receives an ss_session cookie (HttpOnly, Secure, SameSite=Lax, scoped to .scoutsentinel.com). Mutations from a browser session also carry the CSRF token from GET /v1/auth/csrf in the X-CSRF-Token header. Sessions idle out after 30 days of inactivity and expire absolutely after 90 days. Session-only routes (member and API key management, approvals, previews, billing) are marked session below.

Scopes

An API key carries a subset of the actions its creator’s role allows. The key’s effective role is the least privileged role that covers every scope.

Scope Grants
journeys:read Journeys, runs, performance, errors, locations, releases list, locations catalog
journeys:write Create and update journeys
watches:read Watches and their version history
watches:write Create watches and versions, activate, pause, retire
watches:run Run a journey now
events:read Event list, detail and stats
events:write Acknowledge, assign, resolve, dismiss
evidence:read Evidence metadata and download tokens
releases:write Record release markers
reports:read, reports:write Reports
assets:read Exposure findings
integrations:read List integrations
graph:read Shared observation graph

Roles for users are owner, admin, member and viewer; viewers are read-only everywhere, members manage journeys, watches, events and reports, admins add approvals, integrations, members, API keys, secrets, environments and retention, and owners can delete or transfer the organisation.

Conventions

  • JSON request and response bodies, Content-Type: application/json. Every body, path parameter and query string is validated against a published zod schema before a handler runs.
  • IDs are UUID strings (UUIDv7). Timestamps are ISO 8601 with an offset. Durations are milliseconds, sizes are bytes, money is in integer minor units.
  • Environments. Where an endpoint supports an environmentId query parameter, use it to filter that request. A filter is not a key permission boundary: API keys currently apply across the organisation. Check each endpoint’s query schema rather than assuming a global header or default-environment restriction. List environments with GET /v1/environments.
  • Watch definitions are validated with the core schema and cross-field checks described in the step DSL reference; materiality fields with defaults may be omitted on input and are always present in responses.
  • Secrets never round-trip. Tenant secrets return metadata only, integration configs return { "set": true, "last4": "…" } in place of secret fields, and API key secrets appear once in the create response.
  • Outcome and behaviour are always separate fields (outcome, behaviourState); unknown is a first-class outcome with unknownReason.
  • 204 responses carry no body. 202 means the work continues asynchronously and the body tells you what to poll.

Errors

Errors are RFC 9457 problem details with Content-Type: application/problem+json. type is https://api.scoutsentinel.com/problems/<code> and the same code is repeated in the body.

{
  "type": "https://api.scoutsentinel.com/problems/validation_failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "2 field(s) failed validation",
  "code": "validation_failed",
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "errors": [
    { "path": "definition.probes.0.steps.3.type", "code": "custom", "message": "step type \"dns.resolve\" is not allowed in a \"browser\" probe" },
    { "path": "definition.schedule.everySeconds", "code": "too_small", "message": "Too small: expected number to be >=60" }
  ]
}
code Status When
validation_failed 400 Body, params or query failed schema validation; errors[] lists each issue
unauthorized 401 Missing, expired or revoked credential
forbidden 403 Role or scope does not allow the action
csrf_failed 403 Session mutation without a valid X-CSRF-Token
not_found 404 No such resource in this organisation (also used when an environment id is not yours)
conflict 409 The request conflicts with current state, for example activating a retired Watch
payload_too_large 413 Body over the limit
unprocessable 422 Valid shape, but the request cannot be carried out
rate_limited 429 Too many requests; retryAfter (seconds) is in the body and Retry-After in the headers
entitlement_exceeded 422 A plan limit was hit; entitlement: { key, limit, current, plan } explains which
approval_required 4xx The change needs an admin approval before it takes effect; the version is stored and approvalId is returned. The status code for this case is not fixed in the contract yet
internal_error 500 Something on our side; quote traceId to support

Rate limits

Credential endpoints are rate limited per account and per IP. Limits on the resource API depend on your plan and are not published as fixed numbers here; a 429 always carries Retry-After, so back off for that many seconds and retry. Poll endpoints such as journey runs at no more than the Watch’s own interval.

Pagination

List endpoints take cursor and limit (default 25, maximum 100) and return:

{ "items": [], "nextCursor": null }

Pass nextCursor back as cursor until it is null. Cursors are opaque and only valid for the same query.

Time range and filters

  • Ranged reads accept either range (1h, 24h, 7d, 30d) or both from and to (ISO 8601), never both; with neither, the API applies 24h. Responses that depend on the range echo the resolved period: { from, to, preset } so percentages are always labelled.
  • Multi-value filters are comma separated: status=open,acknowledged, severity=critical,high, outcome=fail,unknown.
  • locationKey filters by vantage point using the provider:networkClass:region form, for example cloudflare:edge:lhr.
  • sort and order (asc, desc) are per resource; search matches names and titles.
Resource Filters
Journeys status, behaviourState, channel, subjectId, search, includeArchived, sort (name, status, availability, lastRun, createdAt)
Journey runs outcome, behaviourState, probeType, watchId, locationKey, time range
Events status, severity, kind, category, journeyId, watchId, assetId, assignee (me, unassigned or a user id), search, sort (openedAt, severity, lastObservedAt), time range
Evidence journeyId, watchId, observationId, eventId, kind, stepId, objectAvailable, time range

Resources available to API keys

Method and path Scope Notes
GET /v1/organisation organisation:read Organisation summary
GET /v1/environments organisation:read Environments
GET /v1/entitlements organisation:read Plan limits: Watch count, minimum interval, evidence retention days
GET /v1/locations journeys:read Selectable probe locations with locationKey, class, entitlement and online status
GET, POST /v1/journeys journeys:read, journeys:write Create with subjectId or an inline subject, and optionally an initialWatch definition or a templateId
GET, PATCH /v1/journeys/{journeyId} journeys:read, journeys:write Detail with stats, steps, latest run and coverage
POST /v1/journeys/{journeyId}/run watches:run Run now; 202 with the probe runs that were queued
GET /v1/journeys/{journeyId}/runs journeys:read Observations; GET .../runs/{observationId} returns the full summary, assertion results, evidence and fingerprint diff
GET /v1/journeys/{journeyId}/performance, /errors, /locations journeys:read Timing series, failure history, per-location comparison
GET, POST /v1/journeys/{journeyId}/watches watches:read, watches:write Watches on a journey; POST takes { name, definition, activate? }
GET, PATCH /v1/watches/{watchId} watches:read, watches:write Current definition, requiredSecrets, missingSecrets
POST, GET /v1/watches/{watchId}/versions watches:write, watches:read New version { definition, changeNote?, activate? }; the response says whether approval is required
GET /v1/watches/{watchId}/versions/{version} watches:read One version
GET /v1/watches/{watchId}/versions/diff?from=&to= watches:read Structured diff with per-change impact
POST /v1/watches/{watchId}/activate, /pause, /retire watches:write State changes with an optional reason
GET /v1/events, GET /v1/events/stats events:read Work queue and counts by status, severity and kind
GET /v1/events/{eventId} events:read Detail with timeline, comparison, coverage and explanation
POST /v1/events/{eventId}/acknowledge, /assign, /resolve, /dismiss events:write Dismiss takes a reason: expected_change, test_traffic, duplicate, not_actionable, false_positive, other
GET /v1/evidence, GET /v1/evidence/{evidenceId} evidence:read Metadata only
POST /v1/evidence/{evidenceId}/download-token evidence:read Short-lived signed URL; writes an access audit row
GET /v1/integrations integrations:read Configured integrations, secrets redacted
POST, GET /v1/releases releases:write, journeys:read Release markers for change correlation

Everything else (members, API keys, secrets, retention, integrations management, notification channels, approvals, previews, suppressions, exposure sources, billing) is session-only and lives under the same /v1 prefix.

Example: open Events for one journey

GET /v1/events?journeyId=0192b7d0-4a1e-7c3b-9f2e-6a1d2c3b4a5f&status=open&range=24h HTTP/1.1
Host: api.scoutsentinel.com
Authorization: Bearer ss_live_<prefix>_<secret>
{
  "items": [
    {
      "id": "0192b7e2-1f6a-7d55-8b0c-2e9f4a6b8c1d",
      "title": "Buy online: step 7 \"Complete purchase\" HTTP 502 from payment provider (2 consecutive runs, 3 locations)",
      "detail": "Failing since 2026-09-17T04:25:04Z across 2 runs from cloudflare edge lhr, cloudflare edge fra, cloudflare edge syd.",
      "kind": "outcome",
      "category": "outcome",
      "severity": "critical",
      "status": "open",
      "journey": { "id": "0192b7d0-4a1e-7c3b-9f2e-6a1d2c3b4a5f", "name": "Buy online", "icon": "shopping-cart" },
      "watchId": "0192b7d1-8e2c-7a91-b3d4-5f6e7a8b9c0d",
      "assetId": null,
      "environmentId": "0192b7c0-0000-7000-8000-000000000001",
      "assignee": null,
      "openedAt": "2026-09-17T04:31:12Z",
      "lastObservedAt": "2026-09-17T04:35:09Z",
      "acknowledgedAt": null,
      "resolvedAt": null,
      "observationCount": 6,
      "consecutiveFailures": 2,
      "locations": ["cloudflare:edge:lhr", "cloudflare:edge:fra", "cloudflare:edge:syd"],
      "hasEvidence": true,
      "hasAiExplanation": true,
      "unread": true,
      "feedback": null,
      "supportMinutes": null
    }
  ],
  "nextCursor": null
}

GET /v1/events/{eventId} adds the failure boundary (stepIndex, stepId, stepLabel, reason, assertionId), the observation timeline with each observation’s role (causal, supporting, contradicting, resolving, before), a before and after evidence pair, coverage, and the explanation with its model and prompt version.

Compatibility

The API is versioned in the path. Additive changes (new fields, new enum values announced in advance) do not bump the version. Breaking changes ship as a new version with an overlap period announced at the time.