API documentation

Living Signals REST API v1#

Use the read-only Living Signals REST API to explore news-derived Claims, corroborated Signals, and related Topics. All endpoints use the /api/v1 base path.

Start with GET /api/v1/countries to discover your access. Read GET /api/v1/rubric before you build filters. You can also read this reference on the documentation page, as Markdown, download the OpenAPI 3.1 document, or open the browsable reference.

Changed from the previous contract: /clusters, the level, cluster, and offset parameters, the stable_id, rubric_tags, and topic_stable_id fields, and the countries and languages aliases are not part of v1. There is no compatibility layer.

The model#

Living Signals uses four core concepts:

  1. A Claim is an immutable assertion extracted from one source. It includes article and Source provenance.
  2. A Signal combines corroborating Claims into a discrete, time-bounded occurrence. Signals are the primary exploration resource. Signal detail includes every retrievable Claim in the Signal.
  3. A Topic groups related Signals for browsing.
  4. The rubric describes each Signal with binary attributes. A category groups related attributes and matches when any member attribute is true. Both attributes and categories support multiple values on one Signal.

Annotation layers independently classify Signals through attributed standards or operational vocabularies. Access to each layer is granted separately.

The rubric and annotation layers describe Signals after they form. They do not determine how Claims form Signals or how Signals form Topics.

Getting started#

Send the API key on authenticated requests:

Authorization: Bearer ls_live_<token>

Then follow this path:

  1. Discover your countries, coverage, and annotation layers:

    GET /api/v1/countries
    
  2. Learn the category and attribute vocabulary:

    GET /api/v1/rubric
    
  3. Explore environmental Signals in Cameroon:

    GET /api/v1/signals?country=CM&category=environmental
    
  4. Retrieve one Signal with every supporting Claim:

    GET /api/v1/signals/28eeab9d-47f6-4a2e-bd28-cafe6ad6ca19
    
  5. Poll newly extracted Claims by ingestion time:

    GET /api/v1/claims?since_extracted=24h
    

For paginated requests, follow every non-null nextPage unchanged before you advance a polling watermark.

Authentication and entitlements#

Send an API key in either header:

Authorization: Bearer ls_live_<token>
X-API-Key: ls_live_<token>

Create and manage keys in the Living Signals app at /access#keys. Keys begin ls_live_. Keys issued before 2026-07-21 may begin ls_cust_; they behave identically.

Route Authentication
GET /api/v1 Public
GET /api/v1/rubric Public
GET /api/v1/docs Public
GET /api/v1/openapi.json Public
Every other route in this reference API key required

Customer keys can access only their active country grants. Omit country to use every country available to your key. Requesting any unentitled country rejects the entire request with 403 country_not_entitled. The API never returns a partial country subset. Team keys can access every country.

Annotation layer grants are independent of country grants. Customer Signal responses include only granted layers with stored annotations. Filtering by an ungranted layer rejects the entire request with 403 annotation_layer_not_entitled. Team keys can filter by every registered layer. Set include_annotations=true to include stored annotations in a team response.

Use GET /api/v1/countries to discover annotation layer ids. Use GET /api/v1/annotation-layers to retrieve their code vocabularies.

Enrichment grants are separate from annotation layer and country grants. Customer Signal detail includes satellite imagery only when the organization has the geospatial_v1 enrichment enabled. Team keys can access every enrichment.

Authentication and entitlement failures use distinct codes:

HTTP Code Meaning
401 missing_credentials Neither a Bearer token nor X-API-Key was supplied.
401 invalid_credentials The supplied key is not recognized.
403 key_revoked The key has been revoked.
403 key_expired The key has expired.
403 customer_inactive The customer account is not active.
403 country_not_entitled At least one explicitly requested country is outside the key's grants.
403 annotation_layer_not_entitled An annotation-layer filter names a layer not granted to the customer key.

Conventions#

Dates and timestamps#

Timestamps use RFC 3339 UTC, such as 2026-07-20T21:31:21.628Z. Timestamp fields include extracted_at, article.published_at, first_claim_at, last_claim_at, and formed_at.

event_date, occurred_on, signal_start, and signal_end use YYYY-MM-DD. These fields can be null. occurred_on is the canonical occurrence date for a Claim: event_date when the source states one, else the article's publish date. A null occurred_on means the occurrence time is unknown; such Claims match only since=all. Because event_date comes from source content, it can be imprecise or unreliable. Poll Claims with since_extracted instead.

Every since parameter is an exclusive bound. It accepts:

  • One or more digits followed by h, d, or w. Units are case-insensitive.
  • An ISO date or timestamp beginning with YYYY-MM-DD. Use an RFC 3339 timestamp when you need a time of day.
  • all, which removes the bound.

Valid examples include 24h, 7d, 2w, all, 2026-07-20, and 2026-07-20T21:30:00Z. Invalid suffixes and date strings return 400 invalid_parameter.

The default window#

/signals, /topics, and /claims return the last 90 days when you omit since. Set since to choose a different window, or since=all to search the whole corpus.

The window measures when something happened, not when it was ingested. A story published today about a flood two years ago produces a Claim dated two years ago, and the default window excludes it. Claims filter on occurred_on; Signals filter on signal_end, the latest occurred_on across their Claims. Records whose occurrence time is unknown appear only under since=all.

Passing since_extracted turns the default window off, so a poll returns everything ingested since your last one whatever its occurred_on.

Strict validation#

Query parameters follow these validation rules:

Input Behavior
A supported parameter with a malformed value 400 invalid_parameter.
A parameter recognized elsewhere in v1 but unsupported by this endpoint 400 invalid_parameter with an endpoint hint.
A retired parameter listed below 400 invalid_parameter with its replacement.
A truly unknown parameter name Ignored, consistent with ordinary HTTP query handling.

/docs and /openapi.json serve fixed documents and ignore all query parameters.

Retired parameters are handled explicitly:

Retired parameter Replacement
countries Use repeatable or comma-separated country.
languages Use repeatable or comma-separated language.
offset Pagination is keyset-only; follow nextPage.
level Use the separate /signals and /topics resources.
cluster Use signal on /claims, or use /signals and /topics directly.

The API applies only safe normalization. It trims country codes and converts them to uppercase. It trims language codes and converts them to lowercase. It removes duplicate repeated or comma-separated values. A positive integer limit above 500 is clamped to 500. Check pagination.limit for the effective page size.

Every known path answers OPTIONS with 204 and Allow: GET, OPTIONS, with or without an API key. Unsupported methods return 405 method_not_allowed with the same Allow header.

Unknown paths return a JSON 404 not_found response. Authentication runs before routing under /api/v1/*. An unknown protected path without a valid key therefore returns 401. Public paths are /api/v1, /rubric, /docs, and /openapi.json.

Error envelope and codes#

Most errors use this envelope:

{
  "error": {
    "code": "invalid_parameter",
    "message": "Unknown rubric category \"climate\". See GET /api/v1/rubric for valid category ids."
  }
}

410 signal_retired is the Signal-detail exception. It uses the structured retirement body documented under GET /api/v1/signals/{id} so integrations can distinguish a durable reference with no successor from an id that never existed.

Error codes:

HTTP Code Meaning
400 invalid_parameter A supported value is malformed, a vocabulary value is unknown, or a recognized parameter is unsupported here.
400 invalid_cursor A cursor is empty, malformed, or has the wrong sort-key shape.
401 missing_credentials No API key was supplied.
401 invalid_credentials The supplied key is not recognized.
403 key_revoked The key has been revoked.
403 key_expired The key has expired.
403 customer_inactive The customer account is not active.
403 country_not_entitled An explicitly requested country is not entitled.
403 annotation_layer_not_entitled An annotation filter uses an ungranted layer.
404 not_found The path or requested resource does not exist.
405 method_not_allowed The path exists but does not support the method; check Allow.
410 signal_retired The Signal id existed but has no active successor.
429 rate_limit_exceeded The customer-key limit was exceeded; check Retry-After.
500 internal_error An unexpected server error occurred.
503 auth_unavailable Authentication could not reach its backing service.
503 release_in_progress A coordinated rubric update is in progress. Retry after the Retry-After interval.

Rate limits#

Customer keys use a sliding-window limit of 240 requests per minute per key by default. Your deployment may use a different limit. Check these headers on authenticated customer responses:

Header Meaning
X-RateLimit-Limit Requests allowed in the current window.
X-RateLimit-Remaining Requests remaining in the current window.
X-RateLimit-Reset Seconds until the current window resets; this is a relative duration, not a Unix timestamp.

A 429 rate_limit_exceeded response also includes Retry-After in seconds. Authentication runs before rate limiting, so 401 responses do not include rate limit headers. Public responses and team-key responses do not include them either. Team keys are not rate limited.

Pagination#

/signals, /topics, /claims, and /sources use keyset pagination:

{
  "nextPage": "https://api.livingsignals.example/api/v1/signals?country=CM&limit=50&cursor=MjEw",
  "pagination": {
    "limit": 50,
    "count": 50
  }
}

nextPage is an absolute URL on a first-party Living Signals host. Follow it unchanged until it is null. Its query string preserves your parameters as you sent them (for example, a lowercase country=cm stays lowercase even though filtering normalizes it) — treat the URL as opaque rather than parsing it. count reports rows on the current page, not total results.

Cursors are base64url-encoded sort keys. Treat them as opaque. Omit cursor to start a page walk. An empty, malformed, or incompatible cursor returns 400 invalid_cursor.

Cursors are not bound to a filter set. Some endpoints share a cursor shape, so reusing a cursor may not fail even when it points to an undefined page position. Never create, edit, or reuse a cursor. Follow the nextPage issued for the exact query you are paging through.

Endpoint reference#

GET /api/v1#

Begin here to find core concepts, authentication rules, pagination guidance, supported parameters, and a recommended integration path.

Parameter Meaning
None

Example:

{
  "name": "Living Signals REST API",
  "version": "v1",
  "model": {
    "data_primitives": "Claims are immutable assertions extracted from one source. Signals combine corroborating Claims into time-bounded occurrences. Explore Signals as the primary resource.",
    "enrichment": "Topics group related Signals for browsing.",
    "filtering_and_organization": "The versioned rubric organizes Signals with categories and attributes. Annotation layers add independent classification vocabularies available to your key."
  },
  "getting_started": [
    {
      "step": 1,
      "request": "GET /api/v1/countries",
      "purpose": "Discover your country access, data coverage, and annotation layers."
    },
    {
      "step": 2,
      "request": "GET /api/v1/rubric",
      "purpose": "Learn the rubric vocabulary before you build filters."
    },
    {
      "step": 3,
      "request": "GET /api/v1/signals?country=CM&category=environmental",
      "purpose": "Explore corroborated occurrences in an available country."
    },
    {
      "step": 4,
      "request": "GET /api/v1/signals/{id}",
      "purpose": "Retrieve a Signal with its complete Claim provenance."
    },
    {
      "step": 5,
      "request": "GET /api/v1/claims?since_extracted=24h",
      "purpose": "Poll /claims with since_extracted. Follow nextPage and deduplicate by Claim id."
    }
  ],
  "documentation": "/api/v1/docs",
  "documentation_note": "Read the public Markdown reference at /api/v1/docs. Download OpenAPI at /api/v1/openapi.json. Browse the rendered reference at /reference. Manage keys at /access#keys.",
  "openapi": "/api/v1/openapi.json",
  "reference": "/reference",
  "auth": {
    "scheme": "Bearer or X-API-Key header",
    "customer_example": "Authorization: Bearer ls_live_<token>",
    "team_example": "Authorization: Bearer ls_live_<token>",
    "key_management": "/access#keys",
    "entitlements": "Customer keys can access granted countries, annotation layers, and enrichments. Requesting an unentitled country or annotation layer returns 403; unentitled imagery is absent from Signal detail and its PNG route returns 404. Internal keys can access every country, layer, and enrichment."
  },
  "rate_limit": {
    "applies_to": "Customer keys.",
    "default": "240 requests per minute for each API key.",
    "response": "HTTP 429 rate_limit_exceeded with Retry-After in seconds.",
    "headers": "Metered responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset."
  },
  "validation": {
    "parameters": "On data endpoints, malformed values and parameters supported by a different endpoint return HTTP 400 invalid_parameter. Unknown parameter names are ignored. The static /docs and /openapi.json endpoints ignore all query parameters.",
    "vocabulary": "Unknown rubric ids and annotation codes return HTTP 400 invalid_parameter. The error points to /rubric or /annotation-layers.",
    "paths": "Unknown paths return a JSON HTTP 404 not_found envelope.",
    "methods": "OPTIONS returns HTTP 204. Other non-GET methods return HTTP 405 method_not_allowed with Allow: GET, OPTIONS."
  },
  "pagination": {
    "next_page": "Follow the absolute nextPage URL unchanged until it is null.",
    "cursor": "Treat cursors as opaque and endpoint-specific. Omit the cursor to start. Empty or incompatible cursors return HTTP 400 invalid_cursor.",
    "response": "pagination contains the effective limit and the number of rows on the current page."
  },
  "endpoints": {
    "GET /api/v1": {
      "description": "Public integration guide and endpoint index.",
      "auth": "public",
      "params": {}
    },
    "GET /api/v1/countries": {
      "description": "Your available countries, Claim coverage, and annotation layer ids.",
      "auth": "API key",
      "params": {}
    },
    "GET /api/v1/rubric": {
      "description": "Rubric categories and attributes for filtering Signals and Topics.",
      "auth": "public",
      "params": {}
    },
    "GET /api/v1/annotation-layers": {
      "description": "Annotation vocabularies available to your key.",
      "auth": "API key",
      "params": {}
    },
    "GET /api/v1/signals": {
      "description": "Corroborated Signals with rubric filters and available annotations.",
      "auth": "API key",
      "params": {
        "country": "Repeat or comma-separate ISO-2 codes to use OR. Omission uses your full country access.",
        "category": "Repeat or comma-separate category ids to match any Signal with a member attribute from any listed category.",
        "attribute": "Repeat or comma-separate attribute ids. Every listed attribute must be true.",
        "attribute_any": "Repeat or comma-separate attribute ids. At least one listed attribute must be true.",
        "glide_v1": "Repeat or comma-separate GLIDE codes to use OR within the layer.",
        "acled_v1": "Repeat or comma-separate ACLED codes to use OR within the layer.",
        "iasc_v1": "Repeat or comma-separate IASC codes to use OR within the layer.",
        "iep_positive_peace_v1": "Repeat or comma-separate IEP Positive Peace pillars to use OR within the layer.",
        "geospatial_v1": "Repeat or comma-separate geospatial imagery-suitability codes to use OR within the layer.",
        "topic": "Parent Topic UUID.",
        "since": "Exclusive bound on when the occurrences happened, using signal_end (latest member occurrence date; Signals with no datable occurrence match only all). Defaults to 90d; pass all for the full corpus. Accepts <n>h, <n>d, <n>w, or an ISO date or timestamp.",
        "limit": "Page size. Defaults to 50 and clamps at 500.",
        "cursor": "Opaque cursor from nextPage.",
        "include_annotations": "For team keys, true includes all stored layers. Customer responses always follow grants."
      }
    },
    "GET /api/v1/signals/{id}": {
      "description": "The canonical active Signal with all of its Claims and entitled attached imagery. Retired merge and split ids resolve to their primary successor.",
      "auth": "API key",
      "params": {
        "id": "Signal UUID.",
        "include_annotations": "For team keys, true includes all stored layers. Customer responses always follow grants."
      }
    },
    "GET /api/v1/signals/{id}/imagery/{kind}.png": {
      "description": "An attached satellite imagery chip for the canonical active Signal when geospatial_v1 enrichment access is enabled.",
      "auth": "API key",
      "params": {
        "id": "Signal UUID.",
        "kind": "Imagery kind, currently post_event."
      }
    },
    "GET /api/v1/topics": {
      "description": "Browse groups of related Signals. Topic ids can change between processing runs, so store Signal or Claim ids instead.",
      "auth": "API key",
      "params": {
        "country": "Repeat or comma-separate ISO-2 codes to use OR.",
        "category": "Match any listed category on one grouped Signal.",
        "attribute": "Require every listed attribute on the same grouped Signal.",
        "attribute_any": "Require any listed attribute on the same grouped Signal.",
        "glide_v1": "Match any listed GLIDE code on the same grouped Signal.",
        "acled_v1": "Match any listed ACLED code on the same grouped Signal.",
        "iasc_v1": "Match any listed IASC code on the same grouped Signal.",
        "iep_positive_peace_v1": "Match any listed IEP Positive Peace pillar on the same grouped Signal.",
        "geospatial_v1": "Match any listed geospatial imagery-suitability code on the same grouped Signal.",
        "since": "Exclusive occurrence-time bound met by one grouped Signal. Defaults to 90d; pass all for the full corpus.",
        "limit": "Page size. Defaults to 50 and clamps at 500.",
        "cursor": "Opaque cursor from nextPage."
      }
    },
    "GET /api/v1/claims": {
      "description": "Extracted Claims with article and Source provenance plus current Signal assignment.",
      "auth": "API key",
      "params": {
        "country": "Repeat or comma-separate ISO-2 codes to use OR.",
        "signal": "Current Signal UUID.",
        "since": "Exclusive occurrence-time bound on occurred_on (event date, else article publish date; unknown-date Claims match only all). Defaults to 90d unless since_extracted is set; pass all for the full corpus.",
        "since_extracted": "Exclusive extracted_at bound for incremental polling. Turns off the default since window so polling stays lossless.",
        "limit": "Page size. Defaults to 50 and clamps at 500.",
        "cursor": "Opaque cursor from nextPage."
      }
    },
    "GET /api/v1/claims/{id}": {
      "description": "One extracted Claim.",
      "auth": "API key",
      "params": {
        "id": "Numeric Claim id."
      }
    },
    "GET /api/v1/sources": {
      "description": "Active Sources by country, with recent Claim coverage.",
      "auth": "API key",
      "params": {
        "country": "Repeat or comma-separate ISO-2 codes to use OR.",
        "language": "Repeat or comma-separate two- or three-letter language codes to use OR.",
        "limit": "Page size. Defaults to 100 and clamps at 500.",
        "cursor": "Opaque cursor from nextPage."
      }
    },
    "GET /api/v1/docs": {
      "description": "Public Markdown API reference.",
      "auth": "public",
      "params": {}
    },
    "GET /api/v1/openapi.json": {
      "description": "Public OpenAPI 3.1 document.",
      "auth": "public",
      "params": {}
    }
  }
}

Responses use Cache-Control: public, max-age=3600.

GET /api/v1/countries#

Call this endpoint first to discover your country access, data coverage, and annotation layers. Countries are ordered by code.

Parameter Meaning
None
Field Meaning
countries Countries available to your key. Customer grants remain visible when country metadata is unavailable.
annotation_layers Layer ids available to your key. Team keys receive layer ids present in stored Signal annotations.
countries[].code ISO 3166-1 alpha-2 country code.
countries[].name Country name, or null when metadata is unavailable.
countries[].status live, in_curation, pending, or archived; null when metadata is unavailable.
countries[].claim_count Retrievable Claims for the country.
countries[].source_count Distinct active Sources available for the country.
countries[].first_claim_at Earliest Claim ingestion timestamp, or null.
countries[].last_claim_at Latest Claim ingestion timestamp, or null.

Example:

{
  "countries": [
    {
      "code": "CM",
      "name": "Cameroon",
      "status": "live",
      "claim_count": 18420,
      "source_count": 37,
      "first_claim_at": "2026-05-02T06:14:20.931Z",
      "last_claim_at": "2026-07-20T21:26:42.133Z"
    }
  ],
  "annotation_layers": ["acled_v1", "geospatial_v1", "glide_v1", "iasc_v1"]
}

Coverage includes only successfully extracted Claims available through /claims. Rejected and failed extraction outcomes do not contribute. A country's claim_count equals a complete country-scoped /claims walk. Its last_claim_at comes from a Claim available through /claims.

Responses use Cache-Control: private, max-age=300.

GET /api/v1/rubric#

Use this public, versioned vocabulary to build category, attribute, and attribute_any filters. An attribute is a binary property that may be true for a Signal. A category groups related attributes and matches when any member attribute is true. Scaled and numeric properties are not returned because they are not graded or stored.

Parameter Meaning
None
Field Meaning
version Current rubric vocabulary version, presently v1.1.
categories[].id Exact value accepted by category.
categories[].label Category display label.
categories[].attributes Ordered member attribute ids.
attributes[].id Exact value accepted by attribute and attribute_any.
attributes[].label Short display label when the attribute belongs to a category, or null.
attributes[].description Definition used to grade the attribute.
attributes[].section Group used to organize related attributes. This value is not filterable.
attributes[].categories Categories that include the attribute. An empty array means it does not belong to a category.

Selected elements from the complete response:

{
  "version": "v1.1",
  "categories": [
    {
      "id": "environmental",
      "label": "Environmental",
      "attributes": [
        "involves_environmental_damage",
        "involves_deforestation",
        "involves_water_contamination",
        "involves_extractive_industry"
      ]
    }
  ],
  "attributes": [
    {
      "id": "involves_environmental_damage",
      "label": "Environmental Damage",
      "description": "General environmental harm, ecosystem damage",
      "section": "harm_type",
      "categories": ["environmental"]
    },
    {
      "id": "involves_air_pollution",
      "label": null,
      "description": "Air quality issues, toxic fumes, emissions, smog, respiratory hazards",
      "section": "harm_type",
      "categories": []
    }
  ]
}

Ids are case-sensitive. Responses use Cache-Control: public, max-age=3600.

GET /api/v1/annotation-layers#

Retrieve the annotation vocabularies available to your key. Customer keys receive only granted layers. An empty layers array means no layers are granted. Team keys receive every registered layer.

Parameter Meaning
None
Field Meaning
layers[].id Versioned layer id used as the Signal filter parameter and annotations key.
layers[].name Display name for the classification vocabulary.
layers[].version Vocabulary version.
layers[].attribution.name Organization that maintains the vocabulary.
layers[].attribution.url Official website for the vocabulary.
layers[].codes[].code Exact, case-sensitive API filter and annotation value.
layers[].codes[].canonical Canonical value or name in the vocabulary.
layers[].codes[].meaning Definition of the code.

Selected codes from one granted layer:

{
  "layers": [
    {
      "id": "glide_v1",
      "name": "GLIDE disaster event types",
      "version": "1",
      "attribution": {
        "name": "GLIDE",
        "url": "https://glidenumber.net"
      },
      "codes": [
        {
          "code": "ff",
          "canonical": "FF — Flash Flood",
          "meaning": "Taxonomy path: natural hazard / flash flood / flash flood. Key phrases: flash flood, sudden flooding, rapid-onset flood, torrential rain flooding, urban flash flood, floodwaters surged, runoff flooding, swept away by flood."
        },
        {
          "code": "fl",
          "canonical": "FL — Flood",
          "meaning": "Taxonomy path: natural hazard / flood / flood. Key phrases: flood, flooding, river overflow, inundation, overflowing river, waterlogged areas, floodwaters, seasonal flooding."
        }
      ]
    }
  ]
}

Each layer's codes array is complete and ordered. Responses use Cache-Control: private, max-age=300.

GET /api/v1/signals#

With query, results use word matching and semantic similarity across the accessible corpus. search.mode is hybrid, or lexical when the embedding service is unavailable. search.reranked indicates whether evidence-based reranking succeeded; false means the base retrieval order is used. search.truncated indicates more matches than the result limit; nextPage is null for search. Search is a relevance-ranked retrieval aid, not an exhaustive export. Without query, the existing paginated listing is unchanged. No results means no matching reports under the selected filters, not that no event occurred.

Explore active, corroborated Signals with rubric and annotation filters. Parameters combine with AND. Values within country, category, attribute_any, and each annotation layer use OR. Multiple attribute values use AND. Results use a stable keyset order.

Parameter Meaning
query Search words and meaning (2–500 characters). Returns up to limit results ranked by relevance. No cursor is accepted with a query; narrow your query or filters when search.truncated is true.
country Filter by ISO-2 country code. Repeat or comma-separate values to use OR. Matching ignores case. Omission uses your full access; any unentitled value rejects the request.
category Filter by exact category id. Repeat or comma-separate ids to match a Signal with any member attribute from any listed category.
attribute Filter by exact attribute id. Repeat or comma-separate ids; every listed attribute must be true.
attribute_any Filter by exact attribute id. Repeat or comma-separate ids; at least one must be true. This group combines with every other filter using AND.
glide_v1 Filter by exact GLIDE code. Repeat or comma-separate codes to use OR within this layer.
acled_v1 Filter by exact ACLED code. Repeat or comma-separate codes to use OR within this layer.
iasc_v1 Filter by exact IASC code. Repeat or comma-separate codes to use OR within this layer.
iep_positive_peace_v1 Filter by exact IEP Positive Peace pillar. Repeat or comma-separate codes to use OR within this layer. Append :strengthening, :eroding, or :mixed to narrow one pillar, as well_functioning_government:eroding.
iep_positive_peace_v1_direction Filter by strengthening, eroding, or mixed. Repeat or comma-separate to use OR. With a pillar filter the direction must hold for that pillar; alone it matches any pillar carrying it.
geospatial_v1 Filter by exact geospatial imagery-suitability code. Repeat or comma-separate codes to use OR within this layer.
topic Filter by current parent Topic UUID. Invalid UUIDs return 400 invalid_parameter.
since Filter by Signals whose latest occurrence falls after this exclusive bound. Defaults to 90d. Use the shared date and window formats, or all.
limit Set the page size. The default is 50; values above 500 are clamped to 500.
cursor Continue with the opaque cursor from nextPage. Omit it to start; empty or invalid values return 400 invalid_cursor.
include_annotations For team keys, set true to include all stored annotations. false or an empty value omits them. Customer responses always follow grants.

Within an annotation layer, a Signal matches any requested code. A Signal without stored data for that layer does not match. Separate annotation layers combine with AND. Rubric filters, annotation filters, country, topic, and since also combine with AND.

Unknown category, attribute, or code values return 400 invalid_parameter. Filtering by an ungranted customer layer returns 403 annotation_layer_not_entitled.

Field Meaning
id Signal UUID, subject to the merge lifecycle described below.
label Human-readable name for the occurrence.
description Longer Signal description, or null.
country_code ISO-2 country code.
signal_start Earliest effective date among the Signal's Claims in YYYY-MM-DD format, or null.
signal_end Latest effective date among the Signal's Claims in YYYY-MM-DD format, or null.
last_claim_at Most recent Claim activity timestamp, or null.
formed_at Signal formation timestamp.
claim_count Claims in the Signal.
source_count Distinct Sources supporting the Signal.
topic Current parent {id, label}, or null.
rubric.version Rubric version used to grade the Signal. Legacy rows use the current vocabulary.
rubric.categories Derived category ids in canonical category order.
rubric.attributes True attribute ids in stored order.
annotations Stored annotations available to your key, keyed by layer id. Each value contains codes and rationale, and directions on layers whose codes name a condition. The field is absent when none are included.

Example:

{
  "signals": [
    {
      "id": "28eeab9d-47f6-4a2e-bd28-cafe6ad6ca19",
      "label": "Flooding disrupts homes and water access in Far North",
      "description": "Seasonal floodwater affected several communities and interrupted safe-water access.",
      "country_code": "CM",
      "signal_start": "2026-07-18",
      "signal_end": "2026-07-20",
      "last_claim_at": "2026-07-20T08:45:00.000Z",
      "formed_at": "2026-07-20T09:10:00.000Z",
      "claim_count": 1,
      "source_count": 1,
      "topic": {
        "id": "18aad58a-73fb-4966-a640-626c79aa7d39",
        "label": "Far North climate and displacement"
      },
      "rubric": {
        "version": "v1.1",
        "categories": ["displacement", "natural_disasters", "environmental"],
        "attributes": [
          "involves_flood",
          "involves_displacement",
          "involves_water_contamination"
        ]
      },
      "annotations": {
        "glide_v1": {
          "codes": ["fl"],
          "rationale": "The Signal describes river flooding affecting several communities."
        },
        "iasc_v1": {
          "codes": ["wash", "shelter"],
          "rationale": "The reported impacts concern safe water and emergency shelter."
        },
        "iep_positive_peace_v1": {
          "codes": ["equitable_distribution_of_resources"],
          "directions": { "equitable_distribution_of_resources": "eroding" },
          "rationale": "Damage to water infrastructure widened the gap in access between affected communities."
        }
      }
    }
  ],
  "nextPage": null,
  "pagination": {
    "limit": 50,
    "count": 1
  }
}

Responses use Cache-Control: private, max-age=60.

GET /api/v1/signals/{id}#

Retrieve the canonical active Signal with all of its retrievable Claims. Claims are ordered by numeric id. If the requested id was retired by a merge or split, the API follows its primary successor and returns the canonical Signal instead.

Parameter Meaning
id Signal UUID. Invalid UUIDs return 400 invalid_parameter.
include_annotations Uses the same annotation behavior as the Signal list.

The response adds claims to the Signal fields returned by the list endpoint. Each embedded Claim uses the list Claim shape. Its signal_id equals the parent Signal id.

When an attached satellite chip exists and the organization has the geospatial_v1 enrichment enabled, the response also includes an imagery array. Each entry contains kind, provider, scene_id, scene_datetime, scene_cloud_cover, aoi_cloud_fraction, bbox as [west, south, east, north], pixel width and height, attribution, an analyst-written caption (a string, or null when none has been written), and the authenticated PNG url. Cloud values are numbers or null. Entries are ordered as a before/after story: pre_event, then post_event, then any derived analysis layer such as burn_map. The entire key is absent without the enrichment grant, while imagery is pending, when no clear scene was found, after a failure, or when imagery does not apply to the Signal.

Example:

{
  "signal": {
    "id": "28eeab9d-47f6-4a2e-bd28-cafe6ad6ca19",
    "label": "Flooding disrupts homes and water access in Far North",
    "description": "Seasonal floodwater affected several communities and interrupted safe-water access.",
    "country_code": "CM",
    "signal_start": "2026-07-18",
    "signal_end": "2026-07-20",
    "last_claim_at": "2026-07-20T08:45:00.000Z",
    "formed_at": "2026-07-20T09:10:00.000Z",
    "claim_count": 1,
    "source_count": 1,
    "topic": {
      "id": "18aad58a-73fb-4966-a640-626c79aa7d39",
      "label": "Far North climate and displacement"
    },
    "rubric": {
      "version": "v1.1",
      "categories": ["displacement", "natural_disasters", "environmental"],
      "attributes": [
        "involves_flood",
        "involves_displacement",
        "involves_water_contamination"
      ]
    },
    "imagery": [
      {
        "kind": "post_event",
        "provider": "sentinel-2-l2a",
        "scene_id": "S2B_20260723T091029_CM",
        "scene_datetime": "2026-07-23T09:10:29.000Z",
        "scene_cloud_cover": 8.42,
        "aoi_cloud_fraction": 0.0312,
        "bbox": [14.2731, 10.5487, 14.3583, 10.6336],
        "width": 512,
        "height": 512,
        "attribution": "Contains modified Copernicus Sentinel data 2026",
        "caption": null,
        "url": "/api/v1/signals/28eeab9d-47f6-4a2e-bd28-cafe6ad6ca19/imagery/post_event.png"
      }
    ],
    "claims": [
      {
        "id": "5210",
        "claim": "Floodwater entered homes and interrupted access to a community water point.",
        "event_date": "2026-07-19",
    "occurred_on": "2026-07-19",
      "occurred_on": "2026-07-19",
        "occurred_on": "2026-07-19",
        "extracted_at": "2026-07-20T08:45:00.000Z",
        "country_code": "CM",
        "geo_scope": "point",
        "place_name": "Mayo Crossing",
        "place_country": "CM",
        "region_code": null,
        "lat": 10.5912,
        "lng": 14.3157,
        "signal_id": "28eeab9d-47f6-4a2e-bd28-cafe6ad6ca19",
        "article": {
          "url": "https://cameroon-observer.example/far-north-flood-update",
          "title": "Communities report flood damage in Far North",
          "published_at": "2026-07-20T07:30:00.000Z"
        },
        "source": {
          "id": "a12e699c-cfa2-4ce9-8c01-bec72963ea91",
          "name": "Cameroon Observer",
          "language": "en",
          "country_code": "CM"
        }
      }
    ]
  }
}

An alias response adds resolved_from beside signal. This abbreviated example focuses on the response wrapper:

{
  "signal": {
    "id": "28eeab9d-47f6-4a2e-bd28-cafe6ad6ca19",
    "label": "Flooding disrupts homes and water access in Far North",
    "claims": []
  },
  "resolved_from": {
    "requested_id": "831ecb98-66b4-4503-b6f7-b66a7477afc0",
    "relation": "merged",
    "resolved_at_depth": 2,
    "overlap": 0.875
  }
}

The actual signal object is the complete canonical payload shown above, including only that Signal's Claims. relation and overlap describe the first transition from the requested id; resolved_at_depth is the number of transitions followed. Treat resolved_from as a prompt to replace the stored reference with signal.id.

If a known id has no active successor, the endpoint returns 410 Gone:

{
  "error": "signal_retired",
  "id": "831ecb98-66b4-4503-b6f7-b66a7477afc0",
  "relation": "dissolved",
  "last_seen": "2026-07-20T09:10:00.000Z",
  "successors": []
}

last_seen is omitted when it cannot be derived. An id that never existed still returns 404 not_found.

An existing Signal outside your country access returns 403 country_not_entitled. Responses use Cache-Control: private, max-age=60.

GET /api/v1/signals/{id}/imagery/{kind}.png#

Retrieve one attached satellite imagery chip. The route resolves retired merge and split ids to the same canonical active Signal as Signal detail, then applies the same country entitlement check. Customer organizations must also have the geospatial_v1 enrichment enabled.

Parameter Meaning
id Signal UUID. Invalid UUIDs return 400 invalid_parameter.
kind Imagery kind. The currently emitted value is post_event.

A missing Signal, unknown imagery kind, pending row, failed row, terminal no-scene row, absent row, or missing enrichment grant returns the same 404 not_found. An existing Signal outside your country access returns 403 country_not_entitled.

Successful responses use Content-Type: image/png, Cache-Control: private, max-age=86400, and include the required attribution in X-Imagery-Attribution.

GET /api/v1/topics#

Topics group related Signals for browsing.

Topic ids are temporary. They can change between processing runs, and the API does not preserve old ids. Store Signal or Claim ids instead. Use filters to find the current Topics.

Results use a stable keyset order. Topic responses do not include rubric or annotations.

Parameter Meaning
country Filter by the Topic's ISO-2 country code. Repeat or comma-separate values to use OR. Matching ignores case.
category Filter by exact category id on a grouped Signal. Repeat or comma-separate ids to use OR.
attribute Require one grouped Signal to have every listed attribute. Repeat or comma-separate exact ids.
attribute_any Require the same grouped Signal to have at least one listed attribute. Repeat or comma-separate exact ids.
glide_v1 Match any listed GLIDE code on the same grouped Signal. Repeat or comma-separate exact codes.
acled_v1 Match any listed ACLED code on the same grouped Signal. Repeat or comma-separate exact codes.
iasc_v1 Match any listed IASC code on the same grouped Signal. Repeat or comma-separate exact codes.
iep_positive_peace_v1 Match any listed IEP Positive Peace pillar on the same grouped Signal. Repeat or comma-separate exact codes.
iep_positive_peace_v1_direction Match any listed direction on the same grouped Signal. Repeat or comma-separate strengthening, eroding, or mixed.
geospatial_v1 Match any listed geospatial imagery-suitability code on the same grouped Signal. Repeat or comma-separate exact codes.
since Filter by Topics grouping a Signal whose latest occurrence falls after this exclusive bound. Defaults to 90d. Use the shared date and window formats, or all.
limit Set the page size. The default is 50; values above 500 are clamped to 500.
cursor Continue with the opaque cursor from nextPage. Omit it to start; empty or invalid values return 400 invalid_cursor.

One active, labeled Signal within a Topic must satisfy every rubric and annotation filter. Two Signals cannot satisfy different parts of the same request. category, attribute_any, and codes within one annotation layer use OR. attribute, separate layers, and separate filter groups use AND.

Field Meaning
id Temporary Topic UUID. Do not store it.
label Topic label.
description Topic description, or null.
country_code ISO-2 country code.
signal_count Active Signals grouped into the Topic.
claim_count Stored number of Claims represented by the Topic.
last_claim_at Most recent Topic activity timestamp, or null.
formed_at Topic formation timestamp.
is_fallback Whether this is the fallback Topic for Signals that do not form a coherent group.

Example:

{
  "topics": [
    {
      "id": "18aad58a-73fb-4966-a640-626c79aa7d39",
      "label": "Far North climate and displacement",
      "description": "Current reporting on seasonal hazards and their community impacts.",
      "country_code": "CM",
      "signal_count": 4,
      "claim_count": 18,
      "last_claim_at": "2026-07-20T08:45:00.000Z",
      "formed_at": "2026-07-20T09:00:00.000Z",
      "is_fallback": false
    }
  ],
  "nextPage": null,
  "pagination": {
    "limit": 50,
    "count": 1
  }
}

Responses use Cache-Control: private, max-age=60.

GET /api/v1/claims#

With query, results use word matching and semantic similarity across the accessible corpus. search.mode is hybrid, or lexical when the embedding service is unavailable. search.reranked indicates whether evidence-based reranking succeeded; false means the base retrieval order is used. search.truncated indicates more matches than the result limit; nextPage is null for search. Search is a relevance-ranked retrieval aid, not an exhaustive export. Without query, the existing paginated listing is unchanged. No results means no matching reports under the selected filters, not that no event occurred.

List immutable, extracted Claims with article, Source, geography, and current Signal assignment. Claims are ordered by numeric id.

Parameter Meaning
query Search words and meaning (2–500 characters). Returns up to limit results ranked by relevance. No cursor is accepted with a query; narrow your query or filters when search.truncated is true.
country Filter by ISO-2 country code. Repeat or comma-separate values to use OR. Matching ignores case. Omission uses your full country access.
signal Filter by current Signal UUID. Invalid UUIDs return 400 invalid_parameter. A valid UUID with no matches returns an empty list.
since Filter by occurrence time (occurred_on) after this exclusive bound. Claims with a null occurred_on match only all. Defaults to 90d unless you pass since_extracted.
since_extracted Filter by extracted_at after this exclusive bound. Use this parameter for incremental polling. It also turns off the default since window.
limit Set the page size. The default is 50; values above 500 are clamped to 500.
cursor Continue with the opaque cursor from nextPage. Omit it to start; empty or invalid values return 400 invalid_cursor.

When both time filters are present, both must match. Claims do not support rubric filters because rubric attributes belong to Signals. A new Claim may not have a Signal assignment yet, so rubric filtering would omit it from a poll.

To retrieve Claims under a current Topic, list its Signals with /signals?topic={id}. Then query Claims by each Signal id.

Field Meaning
id Immutable numeric id serialized as a string because database integers can exceed safe JSON integer ranges.
claim Assertion extracted from the source.
event_date Event date stated by the source in YYYY-MM-DD format, or null.
occurred_on Canonical occurrence date: event_date when stated, else the article publish date. null means the occurrence time is unknown. This is the date since filters on.
extracted_at Ingestion timestamp. Poll with since_extracted.
country_code Country used to ingest and authorize access to the Claim.
geo_scope point, country, region, or null.
place_name Extracted place name, or null.
place_country ISO-2 event-place country, or null.
region_code UN M49 region code for a region-scoped Claim, or null.
lat / lng Coordinates only for a point-scoped Claim when both are available; otherwise both null.
signal_id Current Signal UUID, or null while assignment lags ingestion.
article {url, title, published_at}, or null when source content is unavailable.
source {id, name, language, country_code}, or null when the relationship is unavailable.

Example:

{
  "claims": [
    {
      "id": "5210",
      "claim": "Floodwater entered homes and interrupted access to a community water point.",
      "event_date": "2026-07-19",
    "occurred_on": "2026-07-19",
      "occurred_on": "2026-07-19",
      "extracted_at": "2026-07-20T08:45:00.000Z",
      "country_code": "CM",
      "geo_scope": "point",
      "place_name": "Mayo Crossing",
      "place_country": "CM",
      "region_code": null,
      "lat": 10.5912,
      "lng": 14.3157,
      "signal_id": null,
      "article": {
        "url": "https://cameroon-observer.example/far-north-flood-update",
        "title": "Communities report flood damage in Far North",
        "published_at": "2026-07-20T07:30:00.000Z"
      },
      "source": {
        "id": "a12e699c-cfa2-4ce9-8c01-bec72963ea91",
        "name": "Cameroon Observer",
        "language": "en",
        "country_code": "CM"
      }
    }
  ],
  "nextPage": "https://api.livingsignals.example/api/v1/claims?country=CM&limit=1&cursor=NTIxMA",
  "pagination": {
    "limit": 1,
    "count": 1
  }
}

article is present when the Claim retains a source-content URL. Its title and published_at can independently be null. The API decodes common and numeric HTML entities in article.title.

source is present when the Claim retains a Source id. Its name, language, and country_code can independently be null.

Distinguish the country fields:

Field Meaning
country_code The country used to ingest the Claim. This controls access and may differ from the event location.
place_country The country where the described occurrence happened, when known.
region_code The UN M49 region for a region-scoped Claim when resolved.
source.country_code The outlet's headquarters country. It is often null and can differ from the other country fields.

Geography values:

Value Meaning
point A place-scoped Claim. Coordinates appear only when both are stored.
country A country-scoped Claim. place_country identifies the country.
region A regional Claim. region_code identifies the M49 region when resolution succeeds.
null Geographic precision was not assigned.

Country- and region-scoped Claims do not include coordinates. Do not display them as point markers.

Availability and assignment timing#

Claims rejected or failed during extraction are not available through the API. /countries excludes them from Claim counts and coverage dates.

Extraction and Signal assignment happen separately. A new Claim appears in /claims immediately with signal_id: null. A later processing run may assign it to a Signal. Polling with since_extracted still returns the Claim. Retrieve GET /api/v1/claims/{id} again to get its current Signal assignment.

Polling recipe#

For incremental ingestion, record the poll time immediately before the request. Send it on the next poll as since_extracted=<previous poll time>. Overlap adjacent windows slightly and deduplicate by Claim id. This handles boundary timing, retries, and incomplete page walks.

Follow every non-null nextPage unchanged before you advance the poll time. This keeps the same filters and server-issued cursor across the page walk.

Do not use occurred_on or since as an incremental cursor. occurred_on describes source content. It may be old, null, or unreliable. since filters occurrence time and is not a polling watermark.

Responses use Cache-Control: private, max-age=30.

GET /api/v1/claims/{id}#

Retrieve one Claim in the same shape as a list item. The response wraps it in {"claim": ...}.

Parameter Meaning
id Numeric Claim id. Non-numeric input returns 400 invalid_parameter.

Example:

{
  "claim": {
    "id": "5210",
    "claim": "Floodwater entered homes and interrupted access to a community water point.",
    "event_date": "2026-07-19",
    "occurred_on": "2026-07-19",
    "extracted_at": "2026-07-20T08:45:00.000Z",
    "country_code": "CM",
    "geo_scope": "point",
    "place_name": "Mayo Crossing",
    "place_country": "CM",
    "region_code": null,
    "lat": 10.5912,
    "lng": 14.3157,
    "signal_id": "28eeab9d-47f6-4a2e-bd28-cafe6ad6ca19",
    "article": {
      "url": "https://cameroon-observer.example/far-north-flood-update",
      "title": "Communities report flood damage in Far North",
      "published_at": "2026-07-20T07:30:00.000Z"
    },
    "source": {
      "id": "a12e699c-cfa2-4ce9-8c01-bec72963ea91",
      "name": "Cameroon Observer",
      "language": "en",
      "country_code": "CM"
    }
  }
}

An existing Claim outside your country access returns 403 country_not_entitled. A missing, rejected, or failed Claim returns 404 not_found. Responses use Cache-Control: private, max-age=60.

GET /api/v1/sources#

List active Sources by country with recent Claim coverage. Each row represents one (source, country_code) pair. A Source appears once for each country it covers. Results are ordered by Source id and then country_code.

Parameter Meaning
country Filter by ISO-2 country code. Repeat or comma-separate values to use OR. Matching ignores case. Omission uses your full country access.
language Filter by a two- or three-letter language code. Repeat or comma-separate values to use OR. Matching lowercases input.
limit Set the page size. The default is 100; values above 500 are clamped to 500.
cursor Continue with the opaque cursor from nextPage. Omit it to start; empty or invalid values return 400 invalid_cursor.

Country and language filters combine with AND.

Field Meaning
id Source UUID.
name Source display name.
url Canonical Source URL.
domain Lowercase hostname without a leading www., or null when the URL is invalid.
language Two- or three-letter language code, or null.
country_code Country membership represented by this row, not necessarily Source headquarters.
type Source catalog type string, or null.
claim_count_30d Claims collected for this Source and country during the previous 30 days. This value does not affect ordering.

Example:

{
  "sources": [
    {
      "id": "a12e699c-cfa2-4ce9-8c01-bec72963ea91",
      "name": "Cameroon Observer",
      "url": "https://cameroon-observer.example",
      "domain": "cameroon-observer.example",
      "language": "en",
      "country_code": "CM",
      "type": "newspaper_independent",
      "claim_count_30d": 143
    }
  ],
  "nextPage": null,
  "pagination": {
    "limit": 100,
    "count": 1
  }
}

Source type values:

Value Meaning
blog A blog-format or lightly edited news publisher.
broadcaster A television-first or multi-platform broadcast news outlet.
gov_official An official government publisher or news outlet.
magazine A magazine or periodical journalism outlet.
news_aggregator A service that collects or curates reporting from other publishers.
newspaper_independent An independently controlled newspaper or newsroom.
newspaper_state A state-owned or state-controlled newspaper.
radio A radio-first news outlet.
wire A news agency or wire service that distributes reports to other outlets.

type may contain any string or null. Preserve values you do not recognize. Responses use Cache-Control: private, max-age=300.

GET /api/v1/docs and GET /api/v1/openapi.json#

Use these public endpoints to retrieve the Markdown and OpenAPI references. They serve fixed documents, so query parameters do not change the response.

Parameter Meaning
None
Endpoint Content type Meaning
GET /api/v1/docs text/markdown; charset=utf-8 Public Markdown API reference.
GET /api/v1/openapi.json JSON Public OpenAPI 3.1 route and schema document.

Markdown response:

# Living Signals REST API v1

Use the read-only Living Signals REST API to explore news-derived Claims,
corroborated Signals, and related Topics.

OpenAPI response excerpt:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Living Signals API",
    "version": "v1"
  },
  "paths": {
    "/api/v1/signals": {
      "get": {
        "summary": "List Signals"
      }
    },
    "/api/v1/claims": {
      "get": {
        "summary": "List Claims"
      }
    }
  }
}

Browse the OpenAPI document at /reference. Responses from /docs and /openapi.json use Cache-Control: public, max-age=3600.

Signal lifecycle and id stability#

Claim ids are immutable. Signal ids are durable references. When a merge or split retires an id, GET /api/v1/signals/{id} follows lineage transitively to the primary active successor and returns its full payload with resolved_from. Primary successors are selected by highest stored overlap, then by the latest transition time.

Claims in the response always belong to the canonical Signal; the API does not combine Claims from historical identities. When resolved_from appears, update the stored reference to the returned signal.id.

If an id existed but no active successor remains, the detail endpoint returns the structured 410 signal_retired body with an empty successors array. An id that never existed returns 404 not_found.

A Signal can move to a different Topic without receiving a new Claim. Treat signal.topic as current browsing context, not durable identity.

Signals do not expose updated_at or a since_updated watermark. Incremental Claim polling cannot fully reveal relabels, merges, annotation updates, or Topic changes. Periodically perform a complete Signal sync in addition to polling Claims.

Appendix: rubric vocabulary#

The rubric assigns binary attributes to each Signal. Multiple attributes can be true. Categories group related attributes. A Signal belongs to a category when any member attribute is true, so category membership is derived and can also have multiple values.

GET /api/v1/rubric is the machine-readable source of truth. Its category order and member order match these tables.

violence_harm — Violence & Harm#

Attribute id Label Description
involves_death Deaths/Fatalities Reports deaths or fatalities
involves_physical_injury Injuries Reports physical injuries
involves_sexual_violence Sexual Violence Sexual violence or assault
involves_domestic_violence Domestic Violence Domestic or intimate partner violence
involves_kidnapping Kidnapping Kidnapping or hostage-taking
involves_detention_arrest Detention/Arrest Arrests or detentions
involves_property_destruction Property Destruction Destruction of property
civilians_directly_targeted Civilians Targeted Civilians directly targeted

conflict_intensity — Conflict & Unrest#

Attribute id Label Description
involves_tensions_unrest Tensions/Unrest Social tensions without physical violence
involves_protest_demonstration Protests Protests or demonstrations
involves_low_intensity_violence Skirmishes Low-intensity violence, clashes
involves_firearms Firearms Used Use of firearms or gunfire
involves_explosives Explosives/IEDs Bombs, IEDs, explosions

displacement — Displacement & Migration#

Attribute id Label Description
involves_displacement Displacement People displaced from homes
affects_displaced_persons Affects Refugees/IDPs Impacts on already-displaced

natural_disasters — Natural Disasters#

Attribute id Label Description
involves_flood Flooding Floods, flash floods
involves_drought Drought Drought, water scarcity
involves_storm Storms Hurricanes, cyclones, severe weather
involves_earthquake_geological Earthquakes Earthquakes, geological events
involves_wildfire Wildfires Wildfires, bush fires

health_humanitarian — Health & Humanitarian#

Attribute id Label Description
involves_disease_outbreak Disease Outbreak Epidemics, health emergencies
involves_humanitarian_response Aid Response Humanitarian aid delivery

environmental — Environmental#

Attribute id Label Description
involves_environmental_damage Environmental Damage General environmental harm
involves_deforestation Deforestation Forest destruction, logging
involves_water_contamination Contamination/Pollution Water pollution or scarcity
involves_extractive_industry Extractive Industry Mining, oil/gas impacts

actors — Actors Involved#

Attribute id Label Description
involves_state_military State Military National army, military forces
involves_state_police Police/Security Police, law enforcement
involves_non_state_armed_group Armed Groups Rebels, militias, insurgents
involves_criminal_actors Criminal Actors Gangs, cartels, bandits

affected_populations — Affected Populations#

Attribute id Label Description
affects_women_girls Women & Girls Gender-specific impacts
affects_children Children Children as victims
affects_marginalized_community Marginalized Groups Vulnerable communities

women_peace_security — Women, Peace & Security#

Attribute id Label Description
involves_women_as_actors Women as Actors Women leading, organizing, mediating, advocating
involves_gender_policy_action Gender Policy Action Government/institutional action on gender equality
involves_gender_targeted_rhetoric Gender-Targeted Rhetoric Gender-based hate speech or incitement

structural — Social Harms#

Attribute id Label Description
involves_discrimination Discrimination Unequal treatment
involves_marginalization Marginalization Social exclusion
involves_dispossession Dispossession Loss of land/resources
involves_chronic_deprivation Chronic Deprivation Long-term poverty

positive_signals — Peace, Justice & Reform#

Attribute id Label Description
involves_peace_process Peace Process Peace negotiations, ceasefires
involves_mediation_dialogue Mediation/Dialogue Conflict resolution efforts
involves_accountability_justice Justice Mechanism Trials, prosecutions, inquiries, transitional justice
involves_policy_reform Institutional Change New laws, policy changes, institutional reforms

Attributes without a category#

These attributes do not belong to a category. You can still filter by them with attribute and attribute_any. They also appear in rubric.attributes. In GET /api/v1/rubric, they have label: null and an empty categories array. The section field groups them by theme.

harm_type#

Attribute id Description
involves_harassment_abuse Harassment, threats, intimidation, psychological abuse, coercive control, exploitation
involves_harmful_traditional_practice FGM/C, forced/child marriage, honor-based violence, widow inheritance
involves_forced_immobility Trapped populations, movement restrictions, inability to evacuate, siege conditions
involves_looting_theft Looting, armed robbery, highway robbery, theft, pillaging
involves_economic_loss Business closures, job losses, crop failures, financial losses, fiscal crisis
involves_service_disruption Power outages, water supply cut, healthcare unavailable, schools closed
involves_reproductive_health Maternal mortality, reproductive health access, pregnancy complications, family planning barriers
involves_psychological_trauma Trauma, hopelessness, fear, despair, mental health crisis, loneliness epidemic
involves_air_pollution Air quality issues, toxic fumes, emissions, smog, respiratory hazards
involves_soil_contamination Heavy metals, toxic waste, soil poisoning, desertification, erosion
involves_biodiversity_loss Species loss, wildlife deaths, endangered species impacts, ecosystem collapse, bushmeat hunting
involves_extreme_temperature Heat wave, extreme cold, temperature-related deaths or impacts
involves_arson Arson, deliberate burning of buildings/property, fire as weapon
involves_crop_failure Failed harvests, crop destruction, agricultural damage, farm losses
involves_livestock_loss Animal deaths, livestock disease, cattle raiding, herder losses
involves_market_disruption Market closures, supply shortages, price spikes, food scarcity

actors#

Attribute id Description
involves_terrorist_designation Groups explicitly labeled as terrorist by governments or international bodies
involves_civilian_perpetrators Mob violence, individual attackers, community violence
involves_foreign_actors Foreign governments, international forces, cross-border actors
involves_corporate_actors Companies, extractive industries, private contractors
involves_conservation_actors Eco-guards, park rangers, conservation officers, environmental police
involves_poachers_illegal_extractors Poachers, illegal loggers, artisanal/illegal miners, smugglers of natural resources
involves_traditional_authorities Chiefs, traditional councils, customary leaders, elders
involves_civil_society NGOs, community organizations, advocacy groups, humanitarian organizations
involves_international_organizations UN agencies, international NGOs, peacekeepers, international monitors

affected_population#

Attribute id Description
affects_general_population Broad impact not targeting specific demographics
affects_specific_ethnic_group Ethnic targeting, inter-ethnic violence, ethnic minorities affected
affects_specific_religious_group Religious targeting, sectarian violence, religious minorities affected
affects_elderly Elderly/aged as victims or disproportionately affected
affects_journalists_activists Press freedom, civil society targeting, activist persecution
affects_healthcare_workers Attacks on health workers, hospitals targeted, medical staff affected

causation#

Attribute id Description
cause_natural Weather, geological events, natural disasters, disease (natural origin)
cause_intentional_human Deliberate attack, planned action, intentional harm
cause_unintentional_human Accidents, negligence, unintended consequences, human error
cause_institutional_systemic Policy failures, structural issues, systemic neglect, institutional causes
cause_infrastructure_failure Road collapse, dam failure, building collapse, utility failure
involves_bureaucratic_delay Administrative delays, permit denials, bureaucratic obstruction, slow response

sector#

Attribute id Description
affects_healthcare_system Hospitals, clinics, health services, medical supplies, healthcare access
affects_education_system Schools closed, education disrupted, students affected
affects_water_sanitation Water access, clean water, sanitation systems, WASH
affects_food_security Food access, agriculture, food prices, hunger, nutrition
affects_shelter_housing Homelessness, housing destruction, shelter needs
affects_transportation Roads, bridges, airports, transit systems, mobility
affects_communications Internet, phones, media, communications blackouts
affects_livelihoods Jobs, businesses, economic activities, income sources
affects_governance Elections, government function, political institutions, rule of law
involves_reintegration DDR programs, ex-combatant reintegration, refugee return, community reintegration
involves_community_resilience Local coping strategies, community-led response, self-organization, mutual aid

verification#

Attribute id Description
official_source_cited Government statements, official reports, institutional data
multiple_sources_mentioned Corroboration from multiple sources mentioned in the content
firsthand_account Direct witness testimony, on-the-ground reports, first-person accounts
numbers_explicitly_estimated Numbers qualified as estimates, approximations, or uncertain

Appendix: annotation layers#

Annotation layers map Signals to attributed, independent classification vocabularies. Each layer is versioned. Annotation layers appear only on Signals and do not affect how Signals or Topics form.

Use GET /api/v1/countries to discover available layer ids in annotation_layers. Use GET /api/v1/annotation-layers to retrieve each layer's version, attribution, and complete code vocabulary. Customer keys receive granted layers. Team keys receive every registered layer.

For a customer key, interpret annotations this way:

Layer granted? Signal value Meaning
Yes Layer key with non-empty codes Graded and stored with the applicable codes.
Yes Layer key with codes: [] Graded and stored, but no code applies.
Yes Layer key absent Grading is not yet stored; treat it as pending.
No Layer key absent The key is not entitled, so the layer is not exposed.

For a team key, omitting include_annotations, sending an empty value, or sending include_annotations=false omits all annotations. An absent layer then says nothing about its grading state. Send include_annotations=true before you use the table as an all-layer view.

Annotation example:

{
  "annotations": {
    "glide_v1": {
      "codes": ["fl"],
      "rationale": "The Signal describes river flooding."
    }
  }
}

API codes use lowercase snake_case. GLIDE uses lowercase two-letter codes. The _v1 suffix pins the vocabulary version. Discover codes from /annotation-layers instead of assuming that versions share the same values.

glide_v1 — GLIDE disaster event types#

GLIDE is the GLobal IDEntifier number disaster-event taxonomy. API values are lowercase; canonical GLIDE codes are uppercase two-letter codes.

API code Canonical GLIDE value Meaning
cw CW — Cold Wave Cold-wave events, including cold spells, cold snaps, extreme cold, freezing temperatures, and severe frost.
ht HT — Heat Wave Heat-wave events, including extreme, record, scorching, or prolonged heat.
ce CE — Complex Emergency A multifaceted or protracted humanitarian crisis, including conflict-driven emergencies or combined shocks.
dr DR — Drought Prolonged dry conditions such as rainfall deficit, water scarcity, crop failure, or a failed rainy season.
eq EQ — Earthquake Seismic events including earthquakes, quakes, tremors, and aftershocks.
ep EP — Epidemic A disease epidemic or outbreak, including rapid transmission and public-health emergencies.
ec EC — Extratropical Cyclone A mid-latitude or temperate cyclone, frontal storm, winter cyclone, or intense low-pressure system.
fr FR — Fire A fire event such as a structure, urban, building, or industrial fire.
ff FF — Flash Flood Sudden, rapid-onset flooding, often associated with torrential rain or runoff.
fl FL — Flood General flooding or inundation, including river overflow and seasonal flooding.
in IN — Insect Infestation An agricultural insect or pest outbreak, including locust swarms and armyworm infestation.
sl SL — Slide Ground, soil, earth, or slope movement and instability classified as a slide.
ls LS — Land Slide A landslide or land slide, including slope failure, hillside collapse, and debris slide.
ms MS — Mud Slide A mudslide, mudflow, or saturated debris flow, including rain-triggered mud movement.
av AV — Snow Avalanche A snow avalanche, snow-slide, slab release, or snowpack collapse.
st ST — Severe Local Storm A severe local or convective storm, including thunderstorms, hailstorms, and damaging windstorms.
ac AC — Technological Disaster A technological or industrial accident such as a chemical/oil spill, radiological incident, infrastructure failure, or dam failure.
to TO — Tornado A tornado or tornadic vortex, including funnel-cloud and touchdown events.
tc TC — Tropical Cyclone A tropical cyclone, hurricane, typhoon, or tropical storm, including landfall.
ss SS — Storm Surge Storm-driven coastal surge or inundation, including high storm tide and seawater intrusion.
ts TS — Tsunami A tsunami or seismic sea wave, including coastal wave run-up and inundation.
vo VO — Volcano A volcanic eruption or activity, including ash, lava, pyroclastic flow, and tephra fall.
wf WF — Wild Fire A wildfire or uncontrolled forest, bush, vegetation, or grass fire.
vw VW — Violent Wind Destructive high-wind events, including damaging gusts and gale-force winds.
ot OT — Other An emergency, humanitarian situation, or security incident not classified under another GLIDE event type.

acled_v1 — ACLED event sub-types#

ACLED is the Armed Conflict Location & Event Data Project taxonomy. API codes are slugified forms of ACLED's canonical sub-event type names. The event-type family in each meaning also comes from the registered definition.

API code Canonical ACLED sub-event type Meaning
armed_clash Armed clash A Battles sub-event recording an armed clash.
government_regains_territory Government regains territory A Battles sub-event in which government forces regain territory.
non_state_actor_overtakes_territory Non-state actor overtakes territory A Battles sub-event in which a non-state actor takes territory.
chemical_weapon Chemical weapon An Explosions/Remote violence sub-event involving a chemical weapon.
air_or_drone_strike Air/drone strike An Explosions/Remote violence sub-event involving an air or drone strike.
suicide_bomb Suicide bomb An Explosions/Remote violence sub-event involving a suicide bomb.
shelling_artillery_missile Shelling/artillery/missile attack An Explosions/Remote violence sub-event involving shelling, artillery, or a missile attack.
remote_explosive_landmine_ied Remote explosive/landmine/IED An Explosions/Remote violence sub-event involving a remote explosive, landmine, or IED.
grenade Grenade An Explosions/Remote violence sub-event involving a grenade.
sexual_violence Sexual violence A Violence against civilians sub-event specifically involving sexual violence; it does not mean restrictions on women's rights.
attack Attack A Violence against civilians event using ACLED's general Attack sub-event.
abduction_forced_disappearance Abduction/forced disappearance A Violence against civilians sub-event involving abduction or forced disappearance.
peaceful_protest Peaceful protest A Protests sub-event recording a peaceful protest.
protest_with_intervention Protest with intervention A Protests sub-event recording intervention in a protest.
excessive_force_against_protesters Excessive force against protesters A Protests sub-event recording excessive force against protesters.
violent_demonstration Violent demonstration A Riots sub-event recording a violent demonstration.
mob_violence Mob violence A Riots sub-event recording mob violence.
agreement Agreement A Strategic developments sub-event recording an agreement.
arrests Arrests A Strategic developments sub-event recording arrests.
change_to_group_or_activity Change to group/activity A Strategic developments sub-event recording a change to a group or its activity.
disrupted_weapons_use Disrupted weapons use A Strategic developments sub-event for physical weapon seizure or interception, not arms-control talks or agreements.
headquarters_or_base_established Headquarters or base established A Strategic developments sub-event recording establishment of a headquarters or base.
looting_property_destruction Looting/property destruction A Strategic developments sub-event involving looting or property destruction.
non_violent_transfer_of_territory Non-violent transfer of territory A Strategic developments sub-event recording a non-violent territorial transfer.
other_strategic_development Other ACLED's other Strategic developments sub-event.

iasc_v1 — IASC humanitarian coordination clusters#

IASC is the Inter-Agency Standing Committee humanitarian coordination standard. API codes map to its canonical domain names.

API code Canonical IASC value Meaning
protection Protection Safety, dignity, and rights of affected people; child protection, GBV response, mine action, housing/land/property, and rule of law.
health Health Disease outbreaks, medical care, health facilities and workers, immunization, and mental health.
wash Water, Sanitation and Hygiene Safe-water access, sanitation, hygiene, and disease-vector control.
shelter Shelter and NFI Emergency shelter, housing, and non-food relief items.
food_security Food Security Food access and availability, agriculture, livelihoods, and market disruption affecting food.
nutrition Nutrition Malnutrition, therapeutic feeding, and infant and young-child feeding.
education Education Access to schooling, attacks on education, and learning continuity in emergencies.
cccm Camp Coordination and Camp Management Management of displacement sites and camps, site services, and population tracking.
logistics Logistics Supply chains, transport, storage, and access constraints for relief.
early_recovery Early Recovery Restoration of basic services, livelihoods, governance, and infrastructure after crisis.
emergency_telecommunications Emergency Telecommunications Communications infrastructure, connectivity, and data services for response, including telecom or internet blackouts.

iep_positive_peace_v1 — IEP Positive Peace pillars#

Positive Peace is the Institute for Economics & Peace framework describing the attitudes, institutions, and structures that sustain peaceful societies. API codes map to its pillar names.

This layer describes the societal condition a Signal speaks to, not what happened. Positive Peace is defined against the absence of violence, which IEP measures separately through the Global Peace Index, so violent events, natural hazards, and their humanitarian impacts carry no pillar and return an empty codes array. Use glide_v1 and acled_v1 to classify those.

A pillar names the condition under discussion, never a verdict that the condition is in good health. An anti-corruption prosecution and an embezzlement scandal both carry low_levels_of_corruption. Read the code together with directions, which records which way the evidence points for each assigned code:

Direction Meaning
strengthening The Signal shows the condition being built up, improved, or upheld.
eroding The Signal shows it being weakened, damaged, or failing.
mixed The Signal shows both, or the evidence does not point either way.

So a report of extrajudicial killings returns well_functioning_government with direction eroding — the Signal is about the rule of law, and the rule of law is failing.

To filter on direction, attach it to the pillar it applies to:

?iep_positive_peace_v1=well_functioning_government:eroding

The direction is scoped to that pillar, so a Signal whose rule of law is strengthening does not match, even if another of its pillars is eroding. Pillars in the same request are independent — well_functioning_government:eroding,low_levels_of_corruption narrows the first and leaves the second matching in any direction. To ask the looser question across the whole layer, use iep_positive_peace_v1_direction on its own.

A pillar is recorded only where the Signal reveals something about how the condition stands. Routine administration — procurement, workshops, ceremonies, appointments, internal meetings — and internal political party business such as primaries and campaign strategy carry no pillar on their own.

API code Canonical IEP value Meaning
well_functioning_government Well-Functioning Government Public service delivery, political stability, rule of law, courts and administration, elections and transfers of power, and trust in governing institutions.
sound_business_environment Sound Business Environment Economic conditions and the institutions supporting private enterprise, including regulation, banking, competitiveness, and productive economic activity.
equitable_distribution_of_resources Equitable Distribution of Resources Fairness of access to health care, education, and income, and disputes over how public resources are shared between groups or regions.
acceptance_of_the_rights_of_others Acceptance of the Rights of Others Formal rights and the norms behind them, including treatment of minorities, women, migrants, and marginalised groups, discrimination, group grievance, and social exclusion.
good_relations_with_neighbours Good Relations with Neighbours Relations with other countries and between ethnic, religious, and cultural groups, including diplomacy, cross-border cooperation and trade, and reconciliation.
free_flow_of_information Free Flow of Information Media independence and reach, press freedom and treatment of journalists, censorship, internet and telecommunications access, and disinformation.
high_levels_of_human_capital High Levels of Human Capital Education systems and attainment, health and life expectancy, youth employment and training, and research capacity.
low_levels_of_corruption Low Levels of Corruption Bribery, embezzlement and public-sector theft, procurement fraud, entrenched or factionalised elites, and anti-corruption investigations and prosecutions.

The Positive Peace Index indicators beneath these pillars measure countries over years rather than classify events, and are not part of this layer. No IEP index values are reproduced through this API.

geospatial_v1 — Living Signals geospatial imagery suitability#

This focused operational layer records whether a Signal describes already observed physical effects that may support a useful 10 m/pixel optical satellite chip. It is independent of the rubric. Signals about ceremonies, inaugurations, commissionings, openings, financing approvals, plans, appeals, or statements about physical things receive an empty codes array.

API code Canonical value Meaning
visible_landscape_change Visible landscape change The event has already altered the land surface with a footprint plausibly visible in 10 m/pixel optical imagery, such as flooding extent, a burn scar, a landslide, large-scale destruction, active land clearing, or a mass displacement site.
wide_area Wide area The physically affected area spans roughly a hectare or more, such as multiple city blocks, a neighborhood, or a stretch of river or forest, rather than one building, compound, or vehicle.
persistent_trace Persistent trace The visible trace will remain on the ground for two weeks or more, rather than an ephemeral effect such as smoke, crowds, or a temporary closure.