Docs › START HERE › Build Your First Integration

Build Your First Integration

From zero knowledge to live humanitarian data in about ten minutes — the mental model, your first authenticated request, and two real queries.

This is the front door. If you’ve never touched the CLEAR API before, read this page top to bottom. By the end you’ll understand how the data is shaped and you’ll have pulled real records with your own API key. Everything else in these docs — the Queries, Mutations, and Types reference — is there to look things up after you understand the shape of the system.

1. The mental model

CLEAR is a five-tier graph. Raw observations flow in at the bottom and are progressively grouped, classified, and escalated into human-readable advisories. Almost every query you write touches one of these five tiers:

TierWhat it represents
LocationThe administrative hierarchy — country → state → district → point. Everything else hangs off this tree.
SignalA single raw observation ingested from a source (Dataminr, ACLED, GDACS) or filed manually by a field officer.
EventA cluster of related signals, classified by disaster type and bound to a district.
AlertA published advisory raised from a severe event and delivered to subscribed users.
CrisisA user-curated aggregation of related events, enriched by an LLM with a summary, forward scenarios, and a needs analysis.

Two things to internalise. First, the tiers build upward: a Signal links to the Event it belongs to, which links to any Alert raised from it. Second, everything is geolocated — signals, events, and alerts all reference the Location hierarchy. That’s why the “by location” queries are the most powerful way to slice the data.

New to GraphQL? You send one POST request to /graphql with a query naming exactly the fields you want, and you get back exactly those fields — no over-fetching, no guessing at response shapes. The GraphQL Sandbox autocompletes every field as you type, which is the fastest way to explore.

2. Get set up

You need an account and an API key. Both live in the Developer Portal.

1

Create an account

Sign up at the Developer Portal. It takes about ten seconds.

2

Generate an API key

In the portal, open API Keys and create one. Copy it immediately — the full key is shown only once.

Treat your key like a password. Keep it in an environment variable or secrets manager — never in client-side code or version control.

3. Your first request

The me query is the simplest authenticated call: it returns the account your key belongs to. Run it first to confirm your key works before you touch real data. Replace YOUR_API_KEY with the key you just generated:

curl -X POST https://api.clearinitiative.io/graphql \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ me { id email role } }"}'

A successful response echoes your own user back:

{
  "data": {
    "me": {
      "id": "aBc123XyZ",
      "email": "you@example.com",
      "role": "viewer"
    }
  }
}

If me comes back null, your key isn’t being read — double-check the Authorization header and that the key hasn’t been revoked in the portal.

4. Pull real data

Now something useful. Locations are the backbone of the graph, so start there: this lists every country CLEAR tracks (hierarchy level 0), with each one’s id, name, and resident population.

curl -X POST https://api.clearinitiative.io/graphql \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ locations(level: 0) { id name population } }"}'

You’ll get back an array of Location objects. Grab the id of a country that interests you — you’ll feed it into the next query.

level walks the hierarchy: 0 = country, 1 = state/province, 2 = city, and so on. Pass a country’s id to location(id: …) and request children to drill down a level at a time.

5. Slice by location

This is where it gets powerful. eventsByLocation returns every event whose origin, destination, or general location falls within a location and all of its descendants — so one country id gives you everything happening anywhere inside that country. Drop in the id from the previous step:

curl -X POST https://api.clearinitiative.io/graphql \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"query($loc: String!) { eventsByLocation(locationId: $loc) { id title severity types generalLocation { name level } } }","variables":{"loc":"YOUR_LOCATION_ID"}}'

Each Event comes back with a severity from 1 (low) to 5 (severe), its disaster-type types tags, and the place it’s bound to. From any event you can follow the graph further — ask for its signals to see the raw observations underneath, or its alerts to see what was escalated from it.

The plain events, signals, and alerts lists are scoped to a team. If you’re not an admin, pass a teamId for a team you belong to (find yours via the myTeams query). The …ByLocation queries shown here have no such requirement, which is why they’re the easiest place to start.

Where to go next

You now know the shape of the graph and how to authenticate, query, and slice by location. From here:

  • Browse the full Queries and Types reference below — it’s auto-generated from the live schema, so it’s always current.
  • Open the GraphQL Sandbox to build queries interactively with field autocomplete.
  • Read the Authentication section for the browser/cookie flow if you’re building a web app rather than a server integration.
  • Manage your keys anytime in the Developer Portal.

Docs › GET STARTED › Introduction

Introduction

Welcome to the CLEAR API - your gateway to humanitarian intelligence.

The CLEAR API gives you programmatic access to signals, events, alerts, data sources, and geographic location data through a single GraphQL endpoint. Whether you’re building a monitoring dashboard, integrating alerts into your workflow, or analysing humanitarian patterns, this API has you covered.

Everything here is accessible via GraphQL at /graphql. You send a query describing exactly the data you want, and you get back precisely that — nothing more, nothing less.

What You Can Do

FeatureDescription
SignalsAccess raw data items collected from data sources, with location links and metadata.
EventsBrowse grouped signals forming coherent narratives, with location, population, and type data.
AlertsView events escalated for notification, delivered to subscribed users.
Data SourcesDiscover the external data feeds (ACLED, FEWS NET, social media monitors) that supply signals.
LocationsQuery a hierarchical geographic tree — countries, states, cities — with PostGIS geometry.
Disaster TypesLook up disaster classifications with GLIDE numbers.
Feature FlagsCheck runtime feature toggles to adapt your application’s behaviour.
API KeysCreate and manage personal API keys for server-to-server authentication.

Quick Start

Go from zero to your first API response in three steps.

1

Create an account

Head to the Developer Portal and sign up. It takes about ten seconds.

2

Generate an API key

In the portal, go to API Keys and create one. Copy it immediately — you won’t see it again.

3

Make your first query

Send a request with your key in the Authorization header:

curl -X POST https://api.clearinitiative.io/graphql \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ me { id email } }"}'

Want to explore interactively? Open the GraphQL Sandbox to browse the full schema, autocomplete queries, and test requests in your browser.

Authentication

Two ways to authenticate, depending on your use case.

API Keys (server-to-server)

Pass your key as a Bearer token in the Authorization header:

Authorization: Bearer sk_live_your_key_here
Never expose API keys in client-side code or version control. Store them in environment variables or a secrets manager.

Session Cookies (browser apps)

Sign in via the REST auth API. The session cookie is set automatically and sent with subsequent requests:

// Sign in
const res = await fetch('/api/auth/sign-in/email', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify({ email: 'you@example.com', password: '...' }),
});

// Then query (cookie sent automatically)
const data = await fetch('/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify({ query: '{ me { id email } }' }),
});

Queries

NameReturnsDescription
me User Returns the currently authenticated user, or null if not signed in.
users [User!]! List all users.
user User Look up a user by ID.
id: String!
alerts [Alert!]! List alerts. Requires authentication. Admins may omit teamId to list all; non-admins must provide a teamId for a team they belong to.
status: AlertStatusteamId: StringincludeDummy: Boolean
alert Alert Look up an alert by ID. Requires authentication. Non-admins can only access alerts within their team scope.
id: String!
signals [Signal!]! List signals. Requires authentication. includeDummy defaults to false.
teamId: StringincludeDummy: Boolean
signal Signal Look up a signal by ID. Requires authentication. Non-admins can only access signals within their team scope.
id: String!
signalsByLocation [Signal!]! List signals by location. Returns all signals whose origin, destination, or general location is within the given location (including descendants).
locationId: String!
events [Event!]! List events. Requires authentication. includeDummy defaults to false.
teamId: StringincludeDummy: Boolean
event Event Look up an event by ID. Requires authentication. Non-admins can only access events within their team scope.
id: String!
publicEvent PublicEvent Resolve a public share-link to its cached event snapshot. No auth — the (eventId, token) pair from the URL is the gate, and the snapshot only contains the safe fields enumerated on `PublicEvent`. Returns null when the Redis cache has no entry for that pair (link expired, revoked, or evicted under memory pressure — caller treats all three the same).
eventId: String!token: String!
existingPublicEventLink CreatePublicEventLinkResult Look up the most recently-minted public share link for an event, if one still exists in the cache. The Share modal calls this on open so it can reuse an existing link rather than minting a fresh token every time. Returns null when no live link exists — the caller then mints one via `createPublicEventLink`. Requires `requireContentReader` (admin / analyst / viewer); pending users are blocked.
eventId: String!
eventsByLocation [Event!]! List events by location. Returns all events whose origin, destination, or general location is within the given location (including descendants).
locationId: String!
alertsByLocation [Alert!]! List alerts by location. Returns all alerts whose event's location is within the given location (including descendants).
locationId: String!status: AlertStatus
dataSources [DataSource!]! List all data sources.
dataSource DataSource Look up a data source by ID.
id: String!
locations [Location!]! List locations, optionally filtered by hierarchy level (0 = country, 1 = state, etc.).
level: Int
location Location Look up a location by ID.
id: String!
notifications [Notification!]! List notifications, optionally filtered by status.
status: NotificationStatus
notification Notification Look up a notification by ID.
id: String!
featureFlags [FeatureFlag!]! List all feature flags.
featureFlag FeatureFlag Look up a feature flag by its unique key.
key: String!
disasterTypes [DisasterType!]! List all disaster type classifications (flat list of level-3 rows).
disasterType DisasterType Look up a disaster type by ID.
id: String!
disasterTypeHierarchy [DisasterLevel1!]! List disaster types grouped into the 3-level hierarchy (level1 > level2 > level3).
locationMetadata [LocationMetadata!]! List metadata entries for a location, optionally filtered by type. By default only the CURRENT value is returned (validTo is null). Pass current: false to include the full history.
locationId: String!type: Stringcurrent: Boolean
allLocationMetadata [LocationMetadata!]! List every locationMetadata entry of a given type across all locations. By default only current values. Pass current: false for the full history.
type: String!current: Boolean
locationMetadataHistory [LocationMetadata!]! History of a (location, type) pair — newest first. Includes the current row plus every superseded one.
locationId: String!type: String!
myApiKeys [ApiKey!]! List all API keys belonging to the authenticated user. Requires authentication.
myOrganisations [Organisation!]! List organisations the authenticated user belongs to.
organisation Organisation Look up an organisation by ID. Requires membership or global admin.
id: String!
myTeams [Team!]! List teams the authenticated user belongs to.
team Team Look up a team by ID. Requires membership or global admin.
id: String!
pendingInvites [Invitation!]! List pending invitations for an organisation. Requires org admin.
organisationId: String!
invitationByToken InvitationInfo Look up an invitation by token (public — used on accept-invite page).
token: String!
myAlertSubscriptions [AlertSubscription!]! List the authenticated user's alert subscriptions.
alertSubscriptionsByLocation [AlertSubscription!]! List all alert subscriptions for a location (admin only).
locationId: String!
crises [Crisis!]! List all crises.
crisis Crisis Look up a crisis by ID.
id: String!
alertsPage AlertsPage! Paginated alerts feed with severity / location / type / date filters and explicit ordering. Use this instead of `alerts(...)` when the UI needs pages or a totalCount.
input: AlertsPageInput
eventsPage EventsPage! Paginated events feed (same filter shape as alertsPage, plus event-only options). Honours teamId as a location-scope filter.
input: EventsPageInput
signalsPage SignalsPage! Paginated signals feed with source-based filtering.
input: SignalsPageInput
entityStats EntityStats! Cross-entity stats query — returns a total plus optional buckets grouped by type / severity / day / week / month. Filter shape mirrors the page queries so a "current view" can compute its own counts.
input: EntityStatsInput!
nominatimCacheEntry NominatimCacheEntry Look up a cached Nominatim geocoder response by query hash. Returns null when the entry is missing or expired (admin/pipeline only).
queryHash: String!
activityLogs [ActivityLog!]! Paginated activity log. Admin only. Newest first.
filter: ActivityLogFilterInputlimit: Intoffset: Int
activityStats ActivityStats! Aggregated activity counts + per-user + per-day breakdown for the admin dashboard. Admin only. Default window: last 30 days.
from: DateTimeto: DateTime
userEngagement UserEngagementMetrics! Point-in-time user-engagement summary: DAU, WAU, MAU, and the DAU/MAU stickiness ratio. Derived from auth.login activity. Admin only. `asOf` defaults to NOW(); pass a historical timestamp to compute as-of that date.
asOf: DateTime
dauSeries [DauPoint!]! Daily Active Users time series — one point per UTC date in the window. Admin only. Days with zero logins are omitted; the dashboard can backfill them client-side when plotting.
from: DateTime!to: DateTime!
mauSeries [MauPoint!]! Monthly Active Users time series — one point per UTC calendar month in the window. Admin only.
from: DateTime!to: DateTime!
pipelineCountries [PipelineCountry!]! The countries the CLEAR pipeline publishes a Situation Analysis for, with each Country's bounding box. The scheduled publisher reads this to know which countries to run. Requires authentication.
translations [TranslationRow!]! Translation rows currently stored for an entity, one per locale. Admin/pipeline only. Used by clear-pipeline to compare stored source hashes against the canonical row and decide which fields to re-translate.
entityType: String!entityId: String!
translationCoverage [TranslationCoverage!]! Per-(entityType, locale) translation coverage snapshot for the admin dashboard. Admin only. Each row reports canonicalCount (how many entities of that type exist) and translatedCount (how many have a translation row for that locale). Coverage = translated / canonical.
entitiesMissingTranslation [ID!]! IDs of entities of the given type that have NO translation row for the given locale. Admin/pipeline only. Lets the backfill driver enqueue only entities the worker would actually translate, instead of relying on per-task staleness diffs to no-op thousands of already-current rounds. Stale rows (row exists but hashes are out of date) are NOT returned here — they're rare and handled by the per-entity enrichment hooks.
entityType: String!locale: String!

Mutations

NameReturnsDescription
createApiKey CreateApiKeyPayload! Create a new API key for the authenticated user.
input: CreateApiKeyInput!
revokeApiKey ApiKey! Revoke an API key by ID. Only the key owner or an admin can revoke.
id: String!
createPublicEventLink CreatePublicEventLinkResult! Mint a Redis-backed share token for an event. The snapshot of the event's safe-to-share fields is stored under a key derived from the eventId + token and lives for `ttlDays` days (default 30, max 90). Anyone who has the resulting URL can read the snapshot via the `publicEvent(eventId, token)` query. Caller must be able to read the event normally (admin / analyst / viewer); pending users are blocked.
input: CreatePublicEventLinkInput!
revokePublicEventLink Boolean! Invalidate a public share token by deleting the cached snapshot. Idempotent — revoking a missing key returns true. Caller must be an approved user; ownership of the original link is not checked because the link is by definition unauthenticated and the only state to delete is the cache entry itself.
eventId: String!token: String!
createDevUser CreateDevUserResult! Provision a developer account from an approved waitlist application. Creates the user, mints an initial API key (no expiry), issues a long-lived set-password verification token, and sends the welcome email. Requires global `admin`. The CRM write-back is the caller's responsibility.
input: CreateDevUserInput!
rotateDevUserApiKey RotateDevUserApiKeyResult! Revoke every active API key for a dev user and issue a fresh one. Notifies the user by email. Requires global `admin`.
userId: String!
approveUser ApproveUserResult! Approve a self-signed-up user. Flips their role from `pending` to `viewer` (granting read access to signals / events / alerts / crises) and moves their CRM contact from the prospects collection into the approved collection — which fires Exponential's welcome automation. The local role flip is authoritative; CRM updates are best-effort and surface as fields on the result so the admin UI can offer a retry. Requires global `admin`.
userId: String!
requestEmailVerification Boolean! Request an email verification link for the authenticated user.
verifyEmail Boolean! Verify email using a token from the verification link.
token: String!
updateProfile User! Update the authenticated user's profile and notification preferences.
input: UpdateProfileInput!
createAlert Alert! Create an alert from an event, notifying subscribers.
input: CreateAlertInput!
updateAlert Alert! Update an existing alert.
id: String!input: UpdateAlertInput!
deleteAlert Boolean! Delete an alert.
id: String!
archiveStaleAlerts ArchiveStaleAlertsResult! Archive published alerts whose event.lastSignalCreatedAt is older than olderThanDays (default: 14). Sets alerts.status to 'archived'. Admin or pipeline only. Returns the number of rows affected.
olderThanDays: Int
createSignal Signal! Create a signal from a data source.
input: CreateSignalInput!
createManualSignal Signal! Create a manual signal from a field officer, partner, or government source. Persists the signal and sends it to the pipeline for event grouping and auto-escalation.
input: CreateManualSignalInput!
updateSignalSeverity Signal! Update a signal's severity score.
id: String!severity: Int!
updateSignalGeoparsedData Signal! Attach the clear-pipeline geoparser's result to an existing signal. Used for the manual-signal flow, where the signal is created via createManualSignal before the pipeline has a chance to run the geoparser. Stores the structured candidate verbatim; does not change locationId. Admin/pipeline only.
id: String!geoparsedData: JSON!
deleteSignal Boolean! Delete a signal.
id: String!
createEvent Event! Create a new event from signals.
input: CreateEventInput!
updateEvent Event! Update an existing event.
id: String!input: UpdateEventInput!
deleteEvent Boolean! Delete an event.
id: String!
escalateEvent EventEscalation! Escalate an event: creates an alert (published) and records the user escalation. If the event already has a published alert, just records the user escalation. teamId (optional) admits a team_admin or field_coordinator on that team even without a global admin/analyst role — purely an authorisation hint, not stored on the event.
eventId: String!userId: String!teamId: String
createDataSource DataSource! Create a new data source.
input: CreateDataSourceInput!
updateDataSource DataSource! Update an existing data source.
id: String!input: UpdateDataSourceInput!
deleteDataSource Boolean! Delete a data source.
id: String!
createLocation Location! Create a new location.
input: CreateLocationInput!
ensureCountryLocation Location! Idempotently resolve a level-0 Country location by exact name, creating it with a bounding-box MULTIPOLYGON geometry if absent (admin/pipeline only). bbox is [minLng, minLat, maxLng, maxLat]. Returns the (found or created) Country — doubles as the pipeline's name→id resolution.
name: String!bbox: [Float!]!
updateLocation Location! Update an existing location.
id: String!input: UpdateLocationInput!
deleteLocation Boolean! Delete a location.
id: String!
updateLocationGeometry Location! Replace a location's geometry with the given GeoJSON (admin/pipeline only).
id: String!geometry: GeoJSON!
updateLocationPopulation Location! Set a location's cached population (admin/pipeline only).
id: String!population: String!
updateCrisisPopulation Crisis! Set a crisis's populationAffected + populationInArea (admin/pipeline only).
id: String!input: UpdateCrisisPopulationInput!
upsertLocationMetadata LocationMetadata! Create or update a location's metadata entry for a given type (admin/pipeline only). Upsert keyed by (locationId, type).
input: UpsertLocationMetadataInput!
upsertLocationMetadataBatch [LocationMetadata!]! Bulk-upsert multiple (locationId, type, data) rows in a single call (admin/pipeline only). Returns the resulting rows. Rows whose locationId doesn't exist are skipped silently.
inputs: [UpsertLocationMetadataInput!]!
deleteLocationMetadata Boolean! Delete a location's metadata entry for a given type (admin only).
locationId: String!type: String!
findOrCreateLandmarkL4 FindOrCreateLandmarkL4Result! Find an existing level-4 location matching a geoparsed candidate, or create one. Used by the clear-pipeline geoparser to promote a landmark hit (e.g., "Nyala Airport") into a reusable A4 instead of letting the resolver invent a fresh point-location for every signal. When sourceLat and sourceLng are provided, the resolver verifies that the candidate's containing A2 matches the source coord's containing A2 — on mismatch it aborts with abortedReason="different_a2" so the caller can fall back to source coords. Admin/pipeline only.
input: FindOrCreateLandmarkL4Input!
upsertNominatimCache NominatimCacheEntry! Upsert a Nominatim geocoder cache entry (admin/pipeline only). Replaces any existing row with the same queryHash, resetting the TTL.
input: UpsertNominatimCacheInput!
setFeatureFlag FeatureFlag! Set the enabled state of a feature flag, identified by its string key. Upserts the row so the same call works whether the key is already present. Admin only — toggling features is an org-wide change, not a per-user preference. Returns the persisted flag so callers can update local state without an extra round-trip.
key: String!enabled: Boolean!
createNotification Notification! Create a notification for a user.
input: CreateNotificationInput!
createBulkNotifications Int! Create notifications for multiple users at once. Returns the count of notifications created.
input: CreateBulkNotificationsInput!
notifyAlertSubscribers Int! Notify all subscribers of a single alert (immediate frequency). Matches on event types and locations.
input: AlertNotifyInput!
notifyAlertDigest Int! Send a digest notification for multiple alerts to subscribers of the given frequency (daily/weekly/monthly).
input: AlertDigestInput!
deleteNotification Boolean! Delete a notification.
id: String!
markNotificationRead Notification! Mark a notification as read.
id: String!
markAllNotificationsRead Boolean! Mark all notifications as read for the authenticated user.
addFeedback UserFeedback! Add feedback (rating + optional text) to a signal or event.
input: AddFeedbackInput!
deleteFeedback Boolean! Delete your own feedback.
id: String!
addComment UserComment! Add a comment to a signal or event.
input: AddCommentInput!
replyToComment UserComment! Reply to an existing comment.
input: ReplyToCommentInput!
deleteComment Boolean! Delete your own comment.
id: String!
tagUsersInComment UserComment! Tag users in a comment.
commentId: String!userIds: [String!]!
createOrganisation Organisation! Create a new organisation. The creator becomes the first org_admin.
input: CreateOrganisationInput!
updateOrganisation Organisation! Update an existing organisation. Requires org_admin (or platform admin).
id: String!input: UpdateOrganisationInput!
addOrgMember OrgMember! Add a member to an organisation.
orgId: String!userId: String!role: OrgMemberRole
removeOrgMember Boolean! Remove a member from an organisation.
orgId: String!userId: String!
updateOrgMemberRole OrgMember! Change a member's role within an organisation. Requires the caller to be a platform admin or an org_admin of the target organisation.
orgId: String!userId: String!role: OrgMemberRole!
deleteOrganisation Boolean! Delete an organisation and all its teams, members, and invitations. Requires global admin.
id: String!
createTeam Team! Create a new team within an organisation. Requires org_admin (or platform admin).
input: CreateTeamInput!
updateTeam Team! Update an existing team.
id: String!input: UpdateTeamInput!
deleteTeam Boolean! Delete a team.
id: String!
addTeamMember TeamMember! Add a member to a team.
teamId: String!userId: String!role: TeamMemberRole
removeTeamMember Boolean! Remove a member from a team.
teamId: String!userId: String!
updateTeamMemberRole TeamMember! Update a team member's role.
teamId: String!userId: String!role: TeamMemberRole!
setTeamLocations Team! Set the locations a team is scoped to. Replaces all existing locations.
teamId: String!locationIds: [String!]!
setDefaultTeam Team! Set the authenticated user's default team (for frontend convenience).
teamId: String!
inviteUser Invitation! Invite a user to an organisation (and optionally a team). Sends invite email.
input: InviteUserInput!
acceptInvite Boolean! Accept an invitation. Creates user account if new, adds to org and team.
input: AcceptInviteInput!
cancelInvite Boolean! Cancel a pending invitation.
id: String!
resendInvite Invitation! Resend an invitation email (resets expiry to 7 days).
id: String!
requestPasswordReset Boolean! Request a password reset email (public, always returns true).
email: String!
resetPassword Boolean! Reset password using a token from the reset email.
token: String!newPassword: String!
subscribeToAlerts AlertSubscription! Subscribe to alerts for a specific type and location.
input: SubscribeToAlertsInput!
subscribeToAlertsBatch [AlertSubscription!]! Subscribe to alerts for multiple (location × alertType) combinations in a single call. Returns the list of created subscriptions. Duplicates are skipped silently.
input: SubscribeToAlertsBatchInput!
updateAlertSubscription AlertSubscription! Update an existing alert subscription (channel, frequency, active).
id: String!input: UpdateAlertSubscriptionInput!
unsubscribeFromAlerts Boolean! Unsubscribe - deletes the subscription.
id: String!
createCrisisFromEvents Crisis! Create a new crisis from a list of event IDs. Links all provided events to the new crisis.
input: CreateCrisisFromEventsInput!
addEventToCrisis EventCrisis! Add an existing event to an existing crisis. Idempotent - returns the existing link if one already exists.
crisisId: String!eventId: String!
removeEventFromCrisis Crisis Remove an event from a crisis. Recomputes populationAffected from the remaining events and dispatches the enrichment task so title/summary get regenerated to reflect the new event set. If the event being removed is the LAST event, deletes the crisis entirely and returns null. Otherwise returns the updated crisis (title/summary still show pre-removal values until the async enrichment task completes).
crisisId: String!eventId: String!
updateCrisisTitle Crisis! Edit a crisis's title in place. Any authenticated user. Pass an empty string to clear the field.
id: String!title: String!
updateCrisisDescription Crisis! Edit the human-facing description on a crisis. The crisis's summary column stores JSON of the form description+tldr — this mutation updates just the description key and preserves any existing tldr bullets (which the LLM enrichment task generates). Any authenticated user. Pass an empty string to clear the description without disturbing the tldr.
id: String!description: String!
deleteCrisis Boolean! Delete a crisis. Cascades the eventCrises join rows, user feedback, and user comments via the FK constraints. Any authenticated user.
id: String!
addCrisisAttachments Crisis! Append S3 keys to a crisis's attachments list. Idempotent — keys already present in the list are skipped silently. Returns the updated crisis with the new list.
id: String!keys: [String!]!
removeCrisisAttachment Crisis! Remove an S3 key from a crisis's attachments list. Does NOT delete the underlying S3 object (operators can clean those up separately). Returns the updated crisis.
id: String!key: String!
setCrisisNeedsAnalysis Crisis! Set the LLM-generated NRC SAF needs analysis inside the crisis's needs JSONB. Merges generalSummary and sector keys into the existing object so other keys on needs are preserved. Admin/pipeline only.
id: String!generalSummary: [String!]!sector: JSON!
upsertTranslations UpsertTranslationsResult! Upsert one or more per-locale translation rows for an event, crisis, or location. The translated data blob mirrors the canonical entity's JSON shape per locale. Admin/pipeline only.
input: UpsertTranslationsInput!

Types

All types in the schema, auto-generated from the running server.

DateTime scalar

ISO 8601 date-time string (e.g. 2024-01-15T09:30:00.000Z).

GeoJSON scalar

JSON scalar

Arbitrary JSON value — objects, arrays, strings, numbers, booleans, or null.

Upload scalar

File upload scalar (via graphql-upload).

AlertOrderBy enum

ValueDescription
CREATED_DESCNewest first by event.firstSignalCreatedAt.
CREATED_ASCOldest first by event.firstSignalCreatedAt.
SEVERITY_DESCHighest event severity first.
SEVERITY_ASCLowest event severity first.

AlertStatus enum

Publication status of an alert.

ValueDescription
draft
published
archived

Channel enum

Notification channel for alert subscriptions.

ValueDescription
email
sms

DetectionStatus enum

Processing status of a detection (retained for potential future use).

ValueDescription
raw
processed
ignored

EntityKind enum

ValueDescription
signal
event
alert

EventOrderBy enum

ValueDescription
LAST_SIGNAL_DESCNewest signal first (lastSignalCreatedAt).
LAST_SIGNAL_ASCOldest signal first (lastSignalCreatedAt).
CREATED_DESCNewest first by firstSignalCreatedAt.
CREATED_ASCOldest first by firstSignalCreatedAt.
SEVERITY_DESC
SEVERITY_ASC

Frequency enum

How often a user receives alert notifications.

ValueDescription
immediately
daily
weekly
monthly

InvitationStatus enum

Status of an invitation.

ValueDescription
pending
accepted
expired

NotificationStatus enum

ValueDescription
PENDING
DELIVERED
FAILED
READ

OrgMemberRole enum

Role within an organisation.

ValueDescription
org_admin
member

SignalOrderBy enum

ValueDescription
PUBLISHED_DESCNewest first by publishedAt.
PUBLISHED_ASCOldest first by publishedAt.
SEVERITY_DESC
SEVERITY_ASC

StatsGroupBy enum

ValueDescription
noneSingle bucket — just `total`. Use this for "how many X" queries.
typeGroup by event/signal type (event.types[] is unnested; signals use their source name as the type proxy).
severityGroup by integer severity (1-5).
dayGroup by day / week / month of the entity's primary timestamp. Buckets are returned with ISO-8601 keys (`YYYY-MM-DD`, `YYYY-Www`, `YYYY-MM`).
week
month

TeamMemberRole enum

Role within a team.

ValueDescription
team_admin
field_coordinator
team_member

AcceptInviteInput input

FieldTypeDescription
token String!
name String!
password String!

ActivityLogFilterInput input

Filter for the paginated activityLogs query.

FieldTypeDescription
userId String
actionPrefix String Action prefix match — e.g. 'crisis.' returns all crisis-related rows.
action String Exact action match.
resourceType String Coarse resource bucket — 'signal' / 'event' / 'alert' / 'crisis' / 'feedback' / 'session'.
from DateTime
to DateTime

AddCommentInput input

FieldTypeDescription
eventId String Provide exactly one of eventId, signalId, or crisisId.
signalId String
crisisId String
comment String!
tagUserIds [String!] User IDs to tag in the comment.

AddFeedbackInput input

FieldTypeDescription
eventId String Provide exactly one of eventId, signalId, or crisisId.
signalId String
crisisId String
rating Int! Rating from 1 to 5.
text String Optional textual feedback.

AlertDigestInput input

FieldTypeDescription
alertIds [String!]! List of alert IDs to include in the digest.
frequency String! Frequency: daily, weekly, or monthly.

AlertNotifyInput input

FieldTypeDescription
alertId String! Alert ID to notify subscribers about (uses immediate frequency).

AlertsPageInput input

FieldTypeDescription
limit Int Page size — clamped to [1, 100]. Default 25.
offset Int Zero-based row offset. Default 0.
orderBy AlertOrderBy
status AlertStatus
teamId String Apply a team's location-scope filter to the underlying events.
locationId String Restrict to alerts whose event sits under this location (or any of its descendants).
eventTypes [String!] Glide codes — alert event must contain at least one of these in its `types` array. Case-sensitive.
severityMin Int Inclusive lower bound on event severity (1-5).
severityMax Int Inclusive upper bound on event severity (1-5).
from DateTime Filter on event.firstSignalCreatedAt — inclusive.
to DateTime Filter on event.firstSignalCreatedAt — inclusive.
includeDummy Boolean Hide isDummy events when false (default).

CreateAlertInput input

FieldTypeDescription
eventId String! The event ID to create an alert from.
status AlertStatus

CreateApiKeyInput input

Input for creating a new API key.

FieldTypeDescription
name String! A descriptive name for this key (e.g. my-app-prod).
expiresAt DateTime Optional expiration date. Omit for a key that never expires.

CreateBulkNotificationsInput input

FieldTypeDescription
userIds [String!]! List of user IDs to notify.
message String!
notificationType String!
actionUrl String
actionText String

CreateCrisisFromEventsInput input

FieldTypeDescription
title String
summary String
severity Float!
locationId String
needs JSON! Needs as JSON.
eventIds [String!]! Event IDs to link to the newly created crisis (must not be empty).
teamId String Team the crisis is being filed under. When present, the caller may be a team-level team_admin or field_coordinator on that team instead of a platform admin/analyst. Purely an authorisation hint — the crisis itself has no team column; team affiliation is derived from location scope. Ignored for platform-level callers.

CreateDataSourceInput input

FieldTypeDescription
name String!
type String!
isActive Boolean
baseUrl String
infoUrl String

CreateDevUserInput input

Input for `createDevUser`.

FieldTypeDescription
email String! The dev's email address — also the dedup key against existing users.
name String! Display name shown in the welcome email and in the User row.
keyName String Optional descriptive label for the first API key. Defaults to "Initial dev key".

CreateEventInput input

FieldTypeDescription
signalIds [String!]!
title String
description String
descriptionSignals JSON
validFrom String!
validTo String!
firstSignalCreatedAt String!
lastSignalCreatedAt String!
originId String
destinationId String
locationId String
types [String!]!
severity Int Severity score (1–5). Aggregated from signal severities.
populationAffected String
populationDisplaced String Estimated population displaced (BigInt as string).
casualties Int Aggregated casualties for the event (max across constituent signals).
rank Float!
lat Float Latitude for automatic geo-resolution (resolves to nearest location in hierarchy).
lng Float Longitude for automatic geo-resolution.
teamId String Team the event is being filed under. When present, the caller may be a team-level team_admin or field_coordinator on that team instead of a platform admin/analyst. Purely an authorisation hint — the event itself has no team column; team affiliation is derived from location scope. Ignored for platform-level callers.

CreateLocationInput input

FieldTypeDescription
geoId Int
osmId String
pCode String
name String!
level Int!
parentId String

CreateManualSignalInput input

FieldTypeDescription
sourceId String! Data source ID (must be field_officer, partner, or government type).
title String!
description String!
severity Int Severity score (1–5).
url String URL or reference link.
mediaUrls [String!] Media URLs (pre-uploaded via /api/upload endpoint).
media [Upload!] Media files (direct upload via graphql-upload, alternative to mediaUrls).
locationId String
originId String
destinationId String
lat Float Latitude for automatic geo-resolution.
lng Float Longitude for automatic geo-resolution.
metadata JSON Arbitrary internal metadata stored in rawData (e.g. notes, recommendAlert). Not surfaced in the UI - use freely without schema changes.
teamId String Team the signal is being filed under. When present, the caller may be a team-level team_admin or field_coordinator on that team instead of a platform admin/analyst. Purely an authorisation hint — the signal itself has no team column; team affiliation is derived from location scope. Ignored for platform-level callers.

CreateNotificationInput input

FieldTypeDescription
userId String!
message String!
notificationType String!
actionUrl String
actionText String

CreateOrganisationInput input

Fields for creating a new organisation.

FieldTypeDescription
name String! Display name for the organisation.
slug String! URL-friendly identifier. Must be unique.

CreatePublicEventLinkInput input

Input for the `createPublicEventLink` mutation.

FieldTypeDescription
eventId String! The event to share. Caller must be able to read it via the normal `event(id)` resolver — i.e. `requireContentReader` passes.
ttlDays Int Snapshot lifetime in days. Defaults to 30, capped at 90. Smaller values produce shorter URLs by reducing the impact of any future URL-history scraping.

CreateSignalInput input

FieldTypeDescription
sourceId String!
externalId String Stable upstream identifier for idempotent ingestion. If a signal with the same (sourceId, externalId) already exists, createSignal returns the existing row instead of creating a duplicate. Recommended prefix scheme: "dataminr:{alertId}", "gdacs:{eventid}", "acled:{event_id_cnty}".
rawData JSON!
publishedAt String!
collectedAt String
url String
title String
description String
severity Int Severity score (1–5). From data source or estimated by pipeline.
casualties Int Reported casualties for the signal.
media [String!] Media URLs (source URLs for images, videos, etc.).
originId String
destinationId String
locationId String
lat Float Latitude for automatic geo-resolution (resolves to nearest location in hierarchy).
lng Float Longitude for automatic geo-resolution.
geoparsedData JSON Optional output of clear-pipeline's text-based geoparser. Additive enrichment, stored verbatim for downstream comparison against the source's coords. Schema documented on the signals model.
pointName String Human-readable name to use when the resolver has to create a new L4 point location from `lat`/`lng`. Typically the geoparser's top extracted candidate suffixed with " (unresolved)" when the Nominatim lookup failed, so audit views show `al-Obeid (unresolved)` instead of the signal's full paragraph. Ignored when `locationId` is supplied. When omitted, the resolver falls back to a coord-based label like `Point 15.6280, 30.2156`.

CreateTeamInput input

FieldTypeDescription
organisationId String!
name String!
slug String!
description String

EntityStatsInput input

FieldTypeDescription
entity EntityKind!
groupBy StatsGroupBy
teamId String
locationId String
eventTypes [String!]
severityMin Int
severityMax Int
from DateTime
to DateTime
includeDummy Boolean

EventsPageInput input

FieldTypeDescription
limit Int
offset Int
orderBy EventOrderBy
teamId String
locationId String
eventTypes [String!]
severityMin Int
severityMax Int
from DateTime Filter on event.firstSignalCreatedAt — inclusive.
to DateTime
includeDummy Boolean

FindOrCreateLandmarkL4Input input

Input for findOrCreateLandmarkL4. Drives geoparser-based L4 promotion in the signal-ingestion pipeline.

FieldTypeDescription
name String! Display name to use when creating a new L4 (e.g., 'Nyala Airport'). Reuse for kind='landmark' is keyed on a case-insensitive match of this field within the candidate's containing A2.
lat Float! Candidate latitude (from the geocoder).
lng Float! Candidate longitude (from the geocoder).
kind String! 'landmark' or 'admin'. Drives the reuse strategy: landmarks reuse by exact name, admins reuse by proximity (≤100m) within the same A2.
sourceLat Float Source coordinate's latitude. When provided, the resolver verifies the candidate's A2 matches the source's A2 — a mismatch aborts the promotion.
sourceLng Float Source coordinate's longitude. See sourceLat.

InviteUserInput input

FieldTypeDescription
email String!
organisationId String!
role String Organisation role: org_admin, member (default: member).
teams [TeamAssignmentInput!]! Team assignments — at least one team is required. Each entry grants the invitee membership in that team with the given role on acceptance.

LocaleTranslationInput input

Per-locale payload accepted by upsertTranslations. data mirrors the canonical entity's JSON shape exactly for the given locale — e.g. for a crisis it carries the localized title/summary/scenarios/needs with the same keys/nesting the English columns use.

FieldTypeDescription
locale String! BCP-47 lowercased — 'ar', 'fr'. 'en' is rejected (canonical).
data JSON! Translated payload. Shape mirrors the canonical entity per type.
sourceHashes JSON! Per-field SHA-256 hashes of the canonical English source used to produce `data`. Stored so the pipeline can detect which canonical field changed and re-translate only that field on the next pass.

ReplyToCommentInput input

FieldTypeDescription
repliedToCommentId String! ID of the comment to reply to.
comment String!
tagUserIds [String!] User IDs to tag in the reply.

SignalsPageInput input

FieldTypeDescription
limit Int
offset Int
orderBy SignalOrderBy
teamId String
locationId String
sourceNames [String!] Restrict to signals whose source name is in this list (e.g. ["acled","dataminr"]).
severityMin Int
severityMax Int
from DateTime Filter on signal.publishedAt — inclusive.
to DateTime
includeDummy Boolean

SubscribeToAlertsBatchInput input

FieldTypeDescription
locationIds [String!]! One or more location IDs.
alertTypes [String!]! One or more disaster/event types (glideNumbers). A subscription is created for every (location × alertType) pair.
channel Channel!
frequency Frequency!
minSeverity Int Minimum event severity (1-5). Applied to all created subscriptions.

SubscribeToAlertsInput input

FieldTypeDescription
locationId String!
alertType String! Disaster/event type (glideNumber from disaster_types, e.g. 'fl', 'eq').
channel Channel!
frequency Frequency!
minSeverity Int Minimum event severity (1-5) to notify on. Defaults to 1 (all alerts).

TeamAssignmentInput input

One (team, role) assignment passed to inviteUser.

FieldTypeDescription
teamId String!
teamRole TeamMemberRole!

UpdateAlertInput input

FieldTypeDescription
status AlertStatus

UpdateAlertSubscriptionInput input

FieldTypeDescription
channel Channel
frequency Frequency
active Boolean
minSeverity Int Minimum event severity (1-5).

UpdateCrisisPopulationInput input

FieldTypeDescription
populationAffected String Population directly affected by the events (BigInt as string).
populationInArea String Total population residing within the event admin areas (BigInt as string).
title String AI-generated crisis title.
summary String AI-generated crisis summary.
scenarios JSON LLM-generated forward scenarios. Shape: { most_likely, best_case, worst_case, description }.

UpdateDataSourceInput input

FieldTypeDescription
name String
type String
isActive Boolean
baseUrl String
infoUrl String

UpdateEventInput input

FieldTypeDescription
signalIds [String!]
title String
description String
descriptionSignals JSON
validFrom String
validTo String
firstSignalCreatedAt String
lastSignalCreatedAt String
originId String
destinationId String
locationId String
types [String!]
severity Int
populationAffected String
populationDisplaced String Estimated population displaced (BigInt as string).
casualties Int Aggregated casualties for the event (max across constituent signals).
rank Float

UpdateLocationInput input

FieldTypeDescription
geoId Int
osmId String
pCode String
name String
level Int
parentId String

UpdateOrganisationInput input

Fields for updating an existing organisation.

FieldTypeDescription
name String New display name.
slug String New URL-friendly identifier.
isActive Boolean Set active/inactive status.

UpdateProfileInput input

FieldTypeDescription
name String
phoneNumber String
image String
enableInAppNotification Boolean
enableEmailNotification Boolean
enableSMSNotification Boolean
language String Preferred UI language code (BCP-47 / ISO 639-1, e.g. "en", "ar").

UpdateTeamInput input

FieldTypeDescription
name String
slug String
description String

UpsertLocationMetadataInput input

FieldTypeDescription
locationId String!
type String! Type string (e.g. "iom_dtm_displacement").
data JSON! JSON payload for this type.

UpsertNominatimCacheInput input

FieldTypeDescription
queryHash String! SHA-256 hex digest of `<endpoint>:<normalised_query>`. The caller is responsible for computing this — the server doesn't re-hash.
query String! Raw query string for debugging / audit.
endpoint String! The Nominatim endpoint that produced this response.
responseJson JSON! Raw JSON response to cache. Stored verbatim.
status String! 'ok' for a usable result, 'no_result' for an empty result set, 'error' when the geocoder returned an error response. Negative results are cached so we don't re-ask the same dead question.
ttlSeconds Int! How long this entry should remain valid, in seconds. The server computes `expires_at = NOW() + ttl_seconds`.

UpsertTranslationsInput input

FieldTypeDescription
entityType String! One of 'event' | 'crisis' | 'location' (case-insensitive).
entityId String!
translations [LocaleTranslationInput!]! One entry per target locale. Each row is upserted independently.

ActivityCountsByAction object

FieldTypeDescription
login Int!
signalCreateManual Int!
eventCreate Int!
alertCreate Int!
crisisCreate Int!
feedbackCreate Int!
total Int!

ActivityLog object

A single user-initiated action recorded for analytics.

FieldTypeDescription
id String!
user User!
action String! Namespaced action verb — e.g. 'auth.login', 'signal.create_manual', 'event.create', 'alert.create', 'crisis.create', 'feedback.create'.
resourceType String Coarse resource bucket — 'signal' / 'event' / 'alert' / 'crisis' / 'feedback' / 'session'.
resourceId String The created/affected row's id. Null for actions like login.
metadata JSON Per-action context (title, severity, etc.). Shape varies by action.
ipAddress String
userAgent String
createdAt DateTime!

ActivityStats object

Aggregate counts and time-series for the admin dashboard.

FieldTypeDescription
from DateTime! Window the stats cover, in ISO-8601. Mirrored back to the client so a paginated dashboard can know what range its numbers are based on.
to DateTime!
totals ActivityCountsByAction! Total event counts across the window, keyed by action.
byUser [UserActivitySummary!]! Per-user breakdown, ordered by total activity (desc), limited to the top 50 most-active users in the window.
byDay [DailyActivityCount!]! Daily activity counts across the window (UTC dates).

Alert object

An alert created from an event, distributed to subscribed users.

FieldTypeDescription
id String!
event Event! The event this alert was created from.
status AlertStatus!
createdAt DateTime! When the alert row was first created.
updatedAt DateTime! Last time the alert row was updated (e.g. status transition).
userAlerts [UserAlert!]! Users who received this alert.

AlertsPage object

FieldTypeDescription
items [Alert!]!
totalCount Int!
hasMore Boolean!

AlertSubscription object

A user's subscription to alerts of a specific type at a specific location.

FieldTypeDescription
id String!
userId String!
user User!
location Location!
alertType String! Disaster/event type to subscribe to (e.g. 'fl' for flood, 'eq' for earthquake).
active Boolean!
minSeverity Int! Minimum event severity (1-5) to notify on. Alerts with event.severity < minSeverity are suppressed for this user.
channel Channel!
frequency Frequency!
createdAt DateTime!
updatedAt DateTime!

ApiKey object

A personal API key for programmatic access. The full key is only shown once at creation.

FieldTypeDescription
id String!
name String! Descriptive name you chose when creating the key.
prefix String! Short prefix for identification (e.g. sk_live_abc1).
expiresAt DateTime Optional expiration date. Expired keys are rejected automatically.
lastUsedAt DateTime When this key was last used to authenticate a request.
revokedAt DateTime When this key was revoked, if applicable. Revocation is permanent.
createdAt DateTime!
updatedAt DateTime!

ApproveUserResult object

Result of the admin-only `approveUser` mutation. Flips a `pending` user's role to `viewer` and moves the matching CRM contact from the prospects collection into the approved collection, triggering Exponential's welcome automation.

FieldTypeDescription
user User! The approved user (with the new role).
crmMoved Boolean! True when the CRM-side list move completed cleanly. False means the user is approved locally but the CRM record still sits in the prospects list — the admin can retry from /portal/admin without re-approving the user.
crmWarnings [String!]! Non-fatal CRM warnings, if any (e.g. profileType update failed while the list swap succeeded). Empty on the happy path.

ArchiveStaleAlertsResult object

Result of the archiveStaleAlerts bulk mutation.

FieldTypeDescription
alertsArchived Int!

CommentTag object

A tag linking a user to a comment.

FieldTypeDescription
user User!
comment UserComment!

CreateApiKeyPayload object

Returned only from createApiKey. Contains the full plaintext key that will never be retrievable again.

FieldTypeDescription
apiKey ApiKey!
key String! The full API key. Copy this immediately — it cannot be retrieved later.

CreateDevUserResult object

Result of the admin-only `createDevUser` mutation. The plaintext API key is returned only here — it is never stored and cannot be retrieved later. The admin UI surfaces it once so the operator can copy-paste it in case the welcome email bounces.

FieldTypeDescription
user User! The newly-provisioned user.
plaintextKey String! The full plaintext API key (sk_live_…). Save this immediately — it will not be retrievable from the API after this response is read.
welcomeEmailSent Boolean! True when the welcome email was successfully handed off to the email provider.
setPasswordTokenIssued Boolean! True when a long-lived set-password verification token was successfully issued. The welcome email contains a link that consumes the token.

CreatePublicEventLinkResult object

Returned once from `createPublicEventLink`. The token is part of the URL and not retrievable again — same one-time-show semantics as `createApiKey`.

FieldTypeDescription
token String! The plaintext token. Embedded in `url` for convenience.
url String! Pre-built /public/event/<eventId>/<token> URL on the frontend. Hand this straight to the share UI.
expiresAt DateTime! Wall-clock expiry — when the cached snapshot will be dropped from Redis (assuming it isn't evicted earlier or revoked).

Crisis object

A crisis aggregates multiple events into a single coherent narrative.

FieldTypeDescription
id String!
title String
summary String
scenarios JSON Forward-looking scenarios generated by the LLM enrichment task. Shape: { most_likely, best_case, worst_case, description }. Null until the enrichment task runs.
severity Float! Severity score (aggregated from linked events).
generalLocation Location General location of the crisis.
needs JSON! Needs associated with the crisis (JSON structure).
populationAffected String Population directly affected by linked events (BigInt as string).
populationInArea String Total population residing within the admin areas of linked events (BigInt as string).
attachments [String!]! Supporting documents uploaded by users (reports, photos, PDFs). Returned as presigned URLs — the underlying storage is S3 keys, which the resolver converts at read time so links stay short-lived.
events [Event!]! Events that are part of this crisis.
feedbacks [UserFeedback!]! User feedback on this crisis.
comments [UserComment!]! User comments on this crisis.

DailyActivityCount object

FieldTypeDescription
date String! YYYY-MM-DD in UTC.
total Int!
counts ActivityCountsByAction!

DataSource object

An external data source that feeds signals into the system.

FieldTypeDescription
id String!
name String!
type String! Source type identifier (e.g. satellite, sensor, manual).
isActive Boolean!
baseUrl String Base URL of the data source API.
infoUrl String URL with more information about this source.
createdAt DateTime!
updatedAt DateTime!
signals [Signal!]! Signals collected from this data source.

DauPoint object

One day in a DAU time series.

FieldTypeDescription
date String! ISO date (YYYY-MM-DD, UTC).
uniqueUsers Int! Number of distinct users who logged in on this date.

DisasterLevel1 object

A level-1 category with its level-2 groups.

FieldTypeDescription
name String!
groups [DisasterLevel2!]!

DisasterLevel2 object

A level-2 group with its distinct classification codes.

FieldTypeDescription
name String!
codes [String!]! Classification codes belonging to this level-2 group (usually one).
subTypes [DisasterType!]! Level-3 sub-types under this level-2 group.

DisasterType object

A disaster classification in a 3-level hierarchy (level1 > level2 > level3). Events store arrays of codes (GLIDE or CLEAR IDs).

FieldTypeDescription
id String!
disasterType String! Level-3 granular sub-type (e.g. "peaceful protest").
disasterClass String! Level-1 category (legacy — same as level1).
glideNumber String! Classification code — may be a GLIDE number or a CLEAR id.
level1 String! Top-level category (e.g. "natural hazard", "conflict").
level2 String! Mid-level group (e.g. "protests", "battles").
idType String! Identifier system: "glide_number" or "clear_id".

EntityStats object

FieldTypeDescription
total Int! Total matching the filter, regardless of groupBy.
buckets [StatsBucket!]! Buckets when groupBy != none. Empty when groupBy = none.

Event object

An event grouping related signals into a coherent narrative.

FieldTypeDescription
id String!
title String
description String
descriptionSignals JSON LLM-generated signal descriptions as JSON.
validFrom DateTime!
validTo DateTime!
firstSignalCreatedAt DateTime!
lastSignalCreatedAt DateTime!
originLocation Location Origin location of the event.
destinationLocation Location Destination location of the event.
generalLocation Location General location (when no origin/destination).
types [String!]! Event type tags.
severity Int Severity score (1–5). Aggregated from signal severities.
isDummy Boolean! Whether this is seed/demo data.
populationAffected String Estimated population affected.
populationDisplaced String Estimated population displaced by the event (BigInt as string).
casualties Int Aggregated casualties for the event (max across constituent signals).
rank Float!
signals [Signal!]! Signals linked to this event.
alerts [Alert!]! Alerts created from this event.
feedbacks [UserFeedback!]! User feedback on this event.
comments [UserComment!]! User comments on this event.
escalations [EventEscalation!]! Escalations by users.

EventCrisis object

Link between a crisis and an event.

FieldTypeDescription
id String!
crisisId String!
eventId String!
collectedAt DateTime!
crisis Crisis!
event Event!

EventEscalation object

Tracks a user escalating an event, optionally to a crisis.

FieldTypeDescription
id String!
user User!
event Event!
isCrisis Boolean! Whether this has been escalated to a crisis.
validFrom DateTime!
validTo DateTime!

EventsPage object

FieldTypeDescription
items [Event!]!
totalCount Int!
hasMore Boolean!

FeatureFlag object

A feature toggle that controls runtime behavior.

FieldTypeDescription
id Int!
key String! Unique key used to look up this flag (e.g. dark_mode).
enabled Boolean!
updatedAt DateTime!

FindOrCreateLandmarkL4Result object

Outcome of findOrCreateLandmarkL4.

FieldTypeDescription
locationId String The resolved L4 id. Null when the same-A2 safety check aborts.
reused Boolean! True when the call resolved to an existing L4, false when a new one was created. Meaningless when locationId is null.
pointType String Provenance tag of the resolved L4 ('landmark-geocoded', 'gps', etc). Null when the call aborted without producing a row.
abortedReason String Set to 'different_a2' when the candidate and source coords resolve to different containing A2s. Null on success.

Invitation object

An invitation to join an organisation, with one or more team assignments.

FieldTypeDescription
id String!
email String!
organisation Organisation!
team Team Single team — legacy field, populated only for invitations created before the multi-team join existed. New invitations use `teams`.
role String! Organisation role assigned on acceptance.
teamRole String Legacy single-team role. Use `teams` for new invitations.
teams [InvitationTeam!]! Team assignments granted to the invitee on acceptance. Empty list means org-only access (no team).
expiresAt DateTime!
acceptedAt DateTime
invitedBy User!
createdAt DateTime!
status InvitationStatus! Computed from acceptedAt and expiresAt.

InvitationInfo object

Public invitation info returned by token lookup (limited fields).

FieldTypeDescription
id String!
email String!
organisationName String!
teamName String Legacy single-team name (populated only for old invitations).
role String!
teamRole String Legacy single-team role (populated only for old invitations).
teams [InvitationInfoTeam!]! Team assignments the invitee will be granted on acceptance.
expiresAt DateTime!
status InvitationStatus!

InvitationInfoTeam object

One (team, role) assignment as returned by the public token lookup.

FieldTypeDescription
teamId String!
teamName String!
teamRole TeamMemberRole!

InvitationTeam object

One (team, role) assignment attached to an invitation.

FieldTypeDescription
team Team!
teamRole TeamMemberRole!

Location object

A geographic location in a hierarchy (country > state > city, etc.).

FieldTypeDescription
id String!
geoId Int GeoNames identifier.
osmId String OpenStreetMap identifier.
pCode String P-Code identifier.
name String!
level Int! Hierarchy level: 0 = country, 1 = state/province, 2 = city, etc.
geometry GeoJSON Geometry as GeoJSON (Point or MultiPolygon).
parent Location Parent location in the hierarchy.
children [Location!]! Child locations one level below.
ancestorIds [String!]! IDs of all ancestor locations (parent, grandparent, etc.).
ancestors [Location!]! All ancestor locations (parent, grandparent, etc.).
population String Population residing within this location's geometry. Null for point locations or when not yet computed.
pointType String Provenance of this location's geometry. Notable values: - 'landmark-geocoded': L4 created by the pipeline geoparser from a landmark/place name resolved via Nominatim. The location's `name` field is meaningful for display (e.g., 'Nyala Airport'). - 'gps' / 'source-gps' / 'centroid' / 'source-centroid': see schema documentation on the Prisma model. - null: legacy rows (most signal-title L4s from `createPointLocation`).
metadata [LocationMetadata!]! Per-type metadata (IOM DTM, INFORM, etc). Pass a type argument to filter. By default only the current value is returned (validTo is null). Pass current: false to include the full history.
type: Stringcurrent: Boolean
createdAt DateTime! When the row was first created.
updatedAt DateTime! Last write — useful for tracking admin polygon (re-)loads.

LocationMetadata object

Arbitrary per-location metadata keyed by type. History is preserved: writes close the current row (set validTo = now()) and insert a new one. The CURRENT value for a (location, type) is the row where validTo is null.

FieldTypeDescription
id String!
location Location!
type String! Free-form type string (e.g. "iom_dtm_displacement", "acaps_inform").
data JSON! Source-specific payload stored as JSON.
validFrom DateTime! When this value became current.
validTo DateTime When this value was superseded. Null means still current.
createdAt DateTime!
updatedAt DateTime!

MauPoint object

One month in a MAU time series.

FieldTypeDescription
month String! ISO month (YYYY-MM, UTC).
uniqueUsers Int! Number of distinct users who logged in at least once during the calendar month.

NominatimCacheEntry object

A cached Nominatim-protocol geocoder response.

FieldTypeDescription
id String!
queryHash String! SHA-256 hex digest of `<endpoint>:<normalised_query>`.
query String! Raw query string (for debugging only).
endpoint String! The Nominatim endpoint that produced this response (e.g. 'search', 'reverse').
responseJson JSON! Raw JSON response from the geocoder.
status String! Lookup outcome: 'ok' / 'no_result' / 'error'.
fetchedAt DateTime!
expiresAt DateTime!

Notification object

FieldTypeDescription
id String!
user User!
message String!
notificationType String!
actionUrl String
actionText String
status NotificationStatus!
emailNotificationStatus NotificationStatus
smsNotificationStatus NotificationStatus
createdAt DateTime!
updatedAt DateTime!

Organisation object

An organisation that owns teams and has members.

FieldTypeDescription
id String!
name String!
slug String! URL-friendly identifier.
isActive Boolean! Whether this organisation is active. Inactive organisations are hidden from non-admin users.
createdAt DateTime! When this organisation was created.
updatedAt DateTime! When this organisation was last updated.
teams [Team!]! Teams belonging to this organisation.
members [OrgMember!]! Members of this organisation.

OrganisationUser object

Links a user to an organisation with a role.

FieldTypeDescription
id String!
userId String!
organisationId String!
role String!

OrgMember object

Links a user to an organisation with an org-level role.

FieldTypeDescription
id String!
user User!
role String! Organisation-level role: org_admin or member.
createdAt DateTime! When this membership was created.

PipelineCountry object

A country the CLEAR pipeline publishes a Situation Analysis for, with the bounding box used to create its level-0 Country location. bbox order matches ensureCountryLocation: [minLng, minLat, maxLng, maxLat].

FieldTypeDescription
name String!
bbox [Float!]!

PublicEvent object

The slim, unauthenticated view of an event used for share-link rendering. Deliberately a new type — not a slim wrapper around `Event` — so the field set is auditable in one place and field- selection can't accidentally leak signals, comments, alerts, or any other gated data through the public surface. Returned by the `publicEvent(eventId, token)` query when the Redis-cached snapshot for that token is still alive. Cache TTL defaults to 30 days; entries can be evicted earlier under memory pressure, in which case the link expires and the user has to request a new one.

FieldTypeDescription
id String!
title String Localised event title (resolved to the canonical English text when the snapshot was taken — the public page is not locale-aware).
description String Localised event description (canonical English at snapshot time).
severity Float Severity score (1–5) at snapshot time.
validFrom DateTime! Window during which the event is considered valid.
validTo DateTime!
types [String!]! Disaster type codes (e.g. `fl`, `pa`). The frontend resolves these to display names via its own disaster-type catalogue.
primaryLocationName String Human-readable name of the event's primary location (general → origin → destination, first non-null).
primaryLocationCoords [Float!] `[lng, lat]` of the primary location's centroid when that location has a Point geometry. Null for polygon-only locations — the frontend hides the minimap in that case.
populationAffected String Stringified bigint (matches the authenticated `Event` field shape). Null when unknown.
populationDisplaced String
signalPoints [PublicEventSignalPoint!]! `[lng, lat]` for every Point location attached to the event's constituent signals (origin / destination / general fields, deduped by location id). Polygon-only signal locations are omitted. Empty when no signals carry Point geometry — the frontend hides the markers list in that case.
locationPopulation String Population of the event's administrative area, resolved by walking the primary location's ancestor chain in A2 → A1 → A0 order (district → state → country) and picking the deepest non- null. Mirrors the fallback the in-app event page uses so the share card shows the same number. Stringified bigint; null when no level along the chain has a population. See also `locationPopulationLevel` and `locationPopulationName`.
locationPopulationLevel Int Level the population number came from: `2` (district), `1` (state), `0` (country). Null when `locationPopulation` is null.
locationPopulationName String Name of the location the population number is for (e.g. "South Darfur"). Null when `locationPopulation` is null. Lets the frontend label the figure with context — "Population of South Darfur" reads more honestly than a bare number when we fell back to a country-level figure.
locationIdp String Internally-displaced persons count for the event's admin area, sourced from `locationMetadata(type = "iom_dtm_displacement")`. Same A2 → A1 → A0 fallback as `locationPopulation`. Stringified bigint; null when no displacement metadata exists for any level.
locationIdpLevel Int Level the IDP number came from. Null when `locationIdp` is null.
locationIdpName String Name of the location the IDP figure is for. Null when `locationIdp` is null.
sharedAt DateTime! When the share link was minted.
expiresAt DateTime! Soft expiry — the Redis TTL the cached snapshot was written with. The frontend uses this to render "Link expires in N days".

PublicEventSignalPoint object

One Point location attached to one of the event's signals.

FieldTypeDescription
name String Display name of the location (e.g. "Al Fasher"). Often null for raw point-locations like "Point 15.62, 30.21".
lng Float! Geographic longitude.
lat Float! Geographic latitude.

RotateDevUserApiKeyResult object

Result of the admin-only `rotateDevUserApiKey` mutation.

FieldTypeDescription
user User! The user whose key was rotated.
plaintextKey String! The fresh plaintext API key — same one-time-delivery rules as createDevUser.
notificationEmailSent Boolean! True when the rotation-notice email was successfully handed off.

Signal object

A signal derived from a data source.

FieldTypeDescription
id String!
source DataSource! The data source this signal was collected from.
externalId String Stable upstream identifier (e.g. "dataminr:{alertId}"). Used to deduplicate ingestion — (source, externalId) is unique.
publishedAt DateTime!
collectedAt DateTime!
url String
title String
description String
severity Int Severity score (1–5). From data source or estimated by pipeline.
casualties Int Reported casualties for the signal. Sourced from ACLED's fatalities field; for Dataminr, parsed from raw text via regex.
media [String!]! Media URLs (S3 keys for manual uploads, or source URLs for pipeline signals).
isDummy Boolean! Whether this is seed/demo data.
originLocation Location Origin location of the signal.
destinationLocation Location Destination location of the signal.
generalLocation Location General location (when no origin/destination).
events [Event!]! Events this signal is linked to.
feedbacks [UserFeedback!]! User feedback on this signal.
comments [UserComment!]! User comments on this signal.

SignalsPage object

FieldTypeDescription
items [Signal!]!
totalCount Int!
hasMore Boolean!

StatsBucket object

FieldTypeDescription
key String! Bucket label — depends on groupBy: glide code, severity number as string, or ISO date/week/month.
count Int!

Team object

A team within an organisation, scoped to specific locations.

FieldTypeDescription
id String!
name String!
slug String! URL-friendly identifier, unique within the organisation.
description String
organisation Organisation! The organisation this team belongs to.
members [TeamMember!]! Members of this team.
locations [Location!]! Locations this team is scoped to. Empty means global monitoring.
createdAt DateTime!
updatedAt DateTime!

TeamMember object

Links a user to a team with a team-level role.

FieldTypeDescription
id String!
user User!
role String! Team-level role: lead, analyst, or viewer.
createdAt DateTime!

TranslationCoverage object

Per-(entity_type, locale) coverage snapshot used by the admin dashboard to see how much of the catalog has been translated. - canonicalCount: total entities of this type that *could* be translated (i.e. exist in the canonical table). - translatedCount: number with a row in the translations sidecar for this locale. The fraction translatedCount / canonicalCount is the live coverage for that (type, locale) cell — anything < 1.0 means the next read of one of the missing entities by a user on that locale will fall back to canonical English (and trigger the lazy-on-read enqueue, see utils/translation-loader.ts).

FieldTypeDescription
entityType String!
locale String!
canonicalCount Int!
translatedCount Int!

TranslationRow object

A single translation row, returned by the admin/pipeline-only translations(entityType, entityId) query so the pipeline can compare stored source-hashes against the canonical row and decide which fields (if any) need re-translating.

FieldTypeDescription
locale String!
data JSON! Translated payload, same shape as the canonical entity per type.
sourceHashes JSON Per-field SHA-256 hashes of the canonical English source that was used to produce `data`. Null on rows written before hashes were introduced — treat null as "all fields stale".
createdAt DateTime!
updatedAt DateTime!

UpsertTranslationsResult object

Result of upserting one or more locale rows for a given entity. Carries the locales that were written so the caller (typically clear-pipeline) can confirm coverage without a follow-up read.

FieldTypeDescription
entityType String!
entityId String!
locales [String!]!

User object

A registered user with role-based access. PII fields (`email`, `phoneNumber`, `role`, `isActive`) and the private relation set (`alerts`, `notifications`, `organisations`, `teamMemberships`, `feedbacks`, `comments`, `escalations`) are gated server-side: - PII: returned when caller IS the user, is a global admin, OR shares at least one organisation with the user. Otherwise null (PII) or empty array (private relations). - Private relations: returned only when caller IS the user OR is a global admin. Sharing an organisation does NOT grant access to another member's inbox / comment history. Schema makes these fields nullable so the gate can return null when the caller is not authorised, instead of throwing — a logged-in analyst browsing an event's comments shouldn't see a hard error on the commenter's email.

FieldTypeDescription
id String!
email String
name String!
emailVerified Boolean!
phoneNumber String
image String
role String User role: viewer, editor, or admin. Null when caller is not authorised to see this user's role.
isActive Boolean Null when caller is not authorised to see this field.
language String! Preferred UI language code (BCP-47 / ISO 639-1, e.g. "en", "ar"). Defaults to "en".
enableInAppNotification Boolean!
enableEmailNotification Boolean!
enableSMSNotification Boolean!
createdAt DateTime!
updatedAt DateTime!
alerts [UserAlert!]! Alerts received by this user. Empty for non-self / non-admin callers.
notifications [Notification!]! Empty for non-self / non-admin callers.
defaultTeam Team The user's default team (last selected). Null for non-self / non-admin callers.
organisations [OrganisationUser!]! Empty for non-self / non-admin callers.
teamMemberships [TeamMember!]! Empty for non-self / non-admin callers.
feedbacks [UserFeedback!]! Empty for non-self / non-admin callers.
comments [UserComment!]! Empty for non-self / non-admin callers.
escalations [EventEscalation!]! Empty for non-self / non-admin callers.

UserActivitySummary object

FieldTypeDescription
user User!
total Int!
counts ActivityCountsByAction!

UserAlert object

Tracks an alert delivered to a user — view status.

FieldTypeDescription
id String!
user User!
alert Alert!
viewedAt DateTime When the user viewed this alert.

UserComment object

A user comment on a signal, event, or crisis, with reply support.

FieldTypeDescription
id String!
user User!
event Event
signal Signal
crisis Crisis
comment String!
isCommentReply Boolean! Whether this comment is a reply to another comment.
repliedToCommentId String ID of the comment being replied to, if any.
tags [CommentTag!]! Users tagged in this comment.
createdAt DateTime!
updatedAt DateTime!

UserEngagementMetrics object

Point-in-time engagement summary derived from auth.login activity. All three windows are trailing from `asOf` (inclusive).

FieldTypeDescription
asOf DateTime! Reference moment the windows are anchored to. Defaults to NOW() when the caller doesn't specify it.
dau Int! Unique users with at least one login in the trailing 24 hours.
wau Int! Unique users with at least one login in the trailing 7 days.
mau Int! Unique users with at least one login in the trailing 30 days.
dauMauRatio Float! Stickiness ratio (DAU / MAU) as a percentage. Industry benchmark: ≥20% indicates a frequently-returning user base. Returns 0 when MAU is 0 so the field is safe to chart without null-handling.

UserFeedback object

User feedback on a signal, event, or crisis — rating and optional text.

FieldTypeDescription
id String!
user User!
event Event
signal Signal
crisis Crisis
rating Int! Rating from 1 to 5.
text String Optional textual feedback.
createdAt DateTime!
updatedAt DateTime!