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.
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 syncmetadata:read—/api/v1/meta/*vocabulary endpointsevents:read— canonical Event list and detailsignals: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 (requiresobservations:read)GET /api/v1/observations/{public_id}— detail (requiresobservations:read)GET /api/v1/eventsandGET /api/v1/events/{event_id}— canonical Events (requiresevents:read)GET /api/v1/signalsandGET /api/v1/signals/{signal_id}— assessed Signals (requiressignals:read)GET /api/v1/meta/event-types— requiresmetadata:readGET /api/v1/meta/conflicts— requiresmetadata:readGET /api/v1/meta/countries— requiresmetadata: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, otherwisenull. Persisted only — not reverse-geocoded at request time. Not a legal/sovereignty determination.occurred_at— may benull. Precision is oftenunknown.reported_at/ingested_at/sources[].published_atare 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/403do not consume request quota.- Infrastructure
503that occurs before rate limiting does not consume quota. - An authenticated request may consume quota even if it later fails validation with
400/405. 429includesRetry-Afterand does not increment usage beyond the ceiling.- Successful authenticated
GET2xxresponses are metered.OPTIONSis exempt from authentication, rate consumption, and usage metering. - Numeric customer limits are not published until an organisation is provisioned.