Skip to content
WARSTRIKESby Tellurial
For teamsSign in
WARSTRIKES

WarStrikes · Developer docs

B2B Intelligence API

Machine-client HTTP API for commercial integrators. Authenticate with an API key bearer token. Observations are source reports, Events are canonical occurrences, and Signals are assessed intelligence summaries.

Resource identities are distinct. Observations are not canonical physical events. An Event may cite Observation public IDs, and a Signal may cite eligible Event IDs, but each detail resource requires its own entitlement.

OpenAPI 3.1 contract WarStrikes for teams Customer portal

Authentication

Send Authorization: Bearer <api_key>. Keys look like wsk_live_… / wsk_test_…. This is HTTP Bearer of WarStrikes API keys — not OAuth and not JWT.

Any 401 means the credential is unusable. Public authentication failures use the stable reason unauthorized — do not branch business logic on internal key-state diagnostics. Machine clients only: there is no browser CORS grant. Never embed keys in frontend code.

Entitlements

WarStrikes organisation entitlements gate routes (documented on each OpenAPI operation as x-warstrikes-entitlement). These are not OAuth scopes.

  • observations:read — observation list, detail, filters, and sync
  • metadata:read — /api/v1/meta/* vocabulary endpoints
  • events:read — canonical Event list and detail
  • signals:read — canonical Signal list and detail

Entitlements are independent; access to one resource never grants another. Missing entitlement returns 403 with reason forbidden_scope (authentication succeeded). Check Plan & Billing / Entitlements in the Customer Portal, or contact WarStrikes if the capability is not on your plan.

Endpoints

  • GET /api/v1/observations — browse or incremental sync (requires observations:read)
  • GET /api/v1/observations/{public_id} — detail (requires observations:read)
  • GET /api/v1/events and GET /api/v1/events/{event_id} — canonical Events (requires events:read)
  • GET /api/v1/signals and GET /api/v1/signals/{signal_id} — assessed Signals (requires signals:read)
  • GET /api/v1/meta/event-types — requires metadata:read
  • GET /api/v1/meta/conflicts — requires metadata:read
  • GET /api/v1/meta/countries — requires metadata:read

OPTIONS is supported by the runtime for the same paths (prose-only here; not listed as OpenAPI operations). Enabled OPTIONS returns 204 without Access-Control-Allow-Origin.

Canonical Events and Signals

Intelligence list first pages require since and until, accept RFC 3339 bounds at whole-second precision, limit the interval to 31 days, and paginate in ascending time/UUID order. Fractional seconds are rejected. Follow-up pages send only the signed cursor. Detail routes accept no query parameters. Publication eligibility is evaluated live under intelligence-publication-v1, so a cursor does not freeze graph state across requests. Ineligible and nonexistent detail IDs both return 404 not_found. See the OpenAPI contract for filters and exact schemas.

Observation fields

  • country_code — always present; country/territory code derived from the configured Natural Earth boundary dataset for the impact coordinate when known, otherwise null. Persisted only — not reverse-geocoded at request time. Not a legal/sovereignty determination.
  • occurred_at — may be null. Precision is often unknown.
  • reported_at / ingested_at / sources[].published_at are not substitutes for occurrence time.

Browse vs sync

Browse — omit updated_since and cursor. Newest-first; retracted rows hidden unless include_retracted=true. Not full history. Response always includes sync_until, has_more, and next_cursor; in browse mode sync_until is null and next_cursor may be null even when has_more is true.

Sync — supply updated_since on the first page, then follow next_cursor until has_more is false. Sync includes retractions in the window. Cursor continuation may send only cursor and optional limit; combining cursor with other filters returns 400 cursor_param_conflict.

Inclusive checkpoint

T0 = previous checkpoint
GET /api/v1/observations?updated_since=T0
→ first page includes sync_until = T1
→ follow next_cursor until has_more = false
→ only then persist checkpoint = T1
→ next sync uses updated_since=T1

First full sync with no local state should start from updated_since=1970-01-01T00:00:00Z (not browse mode). Do not set checkpoint from max(updated_at).

curl examples

# Observations (requires observations:read)
curl -sS -H "Authorization: Bearer $WARSTRIKES_API_KEY" \
  "https://warstrikes.com/api/v1/observations?updated_since=1970-01-01T00:00:00Z&limit=100"

# Cursor continuation (cursor + optional limit only)
curl -sS -H "Authorization: Bearer $WARSTRIKES_API_KEY" \
  "https://warstrikes.com/api/v1/observations?cursor=CURSOR&limit=100"

# Detail
curl -sS -H "Authorization: Bearer $WARSTRIKES_API_KEY" \
  "https://warstrikes.com/api/v1/observations/PUBLIC_ID"

# Metadata (requires metadata:read — not implied by observations:read)
curl -sS -H "Authorization: Bearer $WARSTRIKES_API_KEY" \
  "https://warstrikes.com/api/v1/meta/event-types"

Sample clients

Repository examples (JSON state file, Python + Node): examples/warstrikes_sync.py and examples/warstrikes_sync.mjs. Credential comes from WARSTRIKES_API_KEY (never a CLI argument).

Errors

Error JSON shape: {"ok":false,"error":"...","reason":"..."}. 401 includes WWW-Authenticate: Bearer. 403 with reason forbidden_scope means authentication succeeded but the organisation lacks the required entitlement (observations:read or metadata:read). 429 includes Retry-After. reason is an open string, not a closed global enum.

Rate limits (external semantics)

  • 401 / 403 do not consume request quota.
  • Infrastructure 503 that occurs before rate limiting does not consume quota.
  • An authenticated request may consume quota even if it later fails validation with 400/405.
  • 429 includes Retry-After and does not increment usage beyond the ceiling.
  • Successful authenticated GET 2xx responses are metered. OPTIONS is exempt from authentication, rate consumption, and usage metering.
  • Numeric customer limits are not published until an organisation is provisioned.