UBuea, PO Box 63, Bueacivic-sentinel@ubuea.cm
API reference

Documentation for a production-facing civic intelligence API.

Everything needed to authenticate, scope access, inspect quotas, and call the Civic Sentinel API. Issue keys from the Developer tab and move into integration with a predictable surface.

Introduction

The Civic Sentinel API is a predictable, versioned REST interface. Every request is made over HTTPS, every response is JSON, and every resource is namespaced under a version prefix so your integration never breaks unexpectedly.

Versioned

All endpoints live under /v1 — breaking changes ship as a new version.

Scoped

Keys carry only the permissions you grant them.

Quota-aware

Every response carries your plan and remaining quota — no extra round-trip.

Base URL

https://api.civicsentinel.io/v1

Quick start

Go from zero to your first authenticated request in four steps.

1

Create an account

Sign up and verify your email to unlock the console.

2

Register an application

Open the Developer tab and create an application to group your keys (Free: 1 app).

3

Issue a scoped key

Pick only the scopes your integration needs. Copy the public key AND secret — the secret is shown only once.

4

Exchange for a token

Use the public key and secret once to receive a short-lived JWT bearer token, then call the API with Authorization.

curl
# Exchange key credentials for a bearer token
curl https://api.civicsentinel.io/v1/developer-token/ \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"api_key":"csk_…","api_secret":"css_…"}'

# Use response.data.access on API calls
curl https://api.civicsentinel.io/v1/profile/ \
  -H "Authorization: Bearer $CIVIC_SENTINEL_TOKEN"

Authentication

Integrations exchange a public key + secret pair for a signed, short-lived JWT bearer token. The public key (csk_…) identifies the application; the secret (css_…) proves ownership. API calls then use only the bearer token until it expires.

X-API-Key

The public key shown when you issued the credential. Safe to include in client-side build artefacts only if paired with origin restrictions on your side.

X-API-Secret

The secret shown only once at issue time. Store it in a server-side secret manager — never in source control or client bundles.

curl
# 1. Exchange credentials
curl https://api.civicsentinel.io/v1/developer-token/ \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"api_key":"csk_…","api_secret":"css_…"}'

# 2. Call the API with the returned access token
curl https://api.civicsentinel.io/v1/alert-destinations/ \
  -H "Authorization: Bearer $CIVIC_SENTINEL_TOKEN" \
  -H "Content-Type: application/json"
Treat the secret like a password and only send it to the token exchange endpoint. If a key leaks, rotate the secret from the Developer tab; already-issued bearer tokens remain usable until their expiry.

The dashboard also authenticates with JWT bearer tokens (Authorization: Bearer …). Developer tokens carry the application id and scopes so most API requests do not need to re-check the API key secret.

Python SDK

Civic Sentinel for Python

Use your API key and secret in your backend code while the SDK handles token exchange, bearer-token refresh, quota headers, retries after expired tokens, and response envelope parsing.

bash
# Install the SDK
pip install civicsentinel

# Install with MCP server support
pip install "civicsentinel[mcp]"
main.py
from civicsentinel import CivicSentinelClient

client = CivicSentinelClient(
    api_key="csk_…",
    api_secret="css_…",
)

countries = client.list_supported_countries()
usage = client.get_usage()
destinations = client.list_alert_destinations()

Token handling

Exchanges credentials at /v1/developer-token/ and refreshes before expiry.

API helpers

Provides generic get/post/patch/delete plus helpers for usage, countries, destinations, subscriptions, dispatches, and risk assessments.

MCP server

Ships a civicsentinel-mcp command with documentation resources and API tools for agents.

bash
# Run the MCP server
export CIVIC_SENTINEL_API_KEY="csk_…"
export CIVIC_SENTINEL_API_SECRET="css_…"
civicsentinel-mcp

API keys & scopes

Each key is scoped to the minimum access it needs. Keys can be revoked and rotated at any time without downtime — issue a new key, deploy it, then revoke the old one. Keys belong to applications; per-day API quota is enforced per application, so spreading traffic across more apps does not give you more headroom unless your plan also raises the per-app limit.

Scoped, per-application keys
Revoke & rotate anytime
Webhook-ready delivery
profile:readRead the account profile.
profile:writeUpdate profile details.
destinations:readList alert destinations.
destinations:writeCreate or edit destinations.
destinations:whatsapp:joinJoin a WhatsApp group destination.
subscriptions:readList alert subscriptions.
subscriptions:writeCreate or cancel alert subscriptions.
dispatches:readRead alert dispatch history.
risk_assessments:readRead country risk assessments.
billing:readRead billing subscriptions and payment events.
countries:readList supported countries.

Endpoints

Core endpoints grouped by resource. Every endpoint requires the scope listed alongside it — a key without that scope receives a 403. Public endpoints (/v1/usage/, /v1/pricing-plans/) accept developer bearer tokens; the token-exchange endpoint accepts the API key pair.

Account

POST/v1/developer-token/Exchange an API key and secret for a short-lived bearer token.API key pair
GET/v1/profile/Retrieve the authenticated account profile.profile:read
PATCH/v1/profile/{id}/Update first name, phone number, or country.profile:write

Alert destinations

GET/v1/alert-destinations/List registered alert destinations.destinations:read
POST/v1/alert-destinations/Register an alert destination (email, webhook).destinations:write
DELETE/v1/alert-destinations/{id}/Remove an alert destination.destinations:write
POST/v1/alert-destinations/join-whatsapp-group/Have the bot join a WhatsApp group and register it as a destination.destinations:whatsapp:join

Alert subscriptions

GET/v1/alert-subscriptions/List active alert subscriptions.subscriptions:read
POST/v1/alert-subscriptions/Subscribe a destination to an alert stream.subscriptions:write

Intelligence

GET/v1/risk-assessments/Fetch the latest country risk assessments.risk_assessments:read
GET/v1/alert-dispatches/Read the alert delivery history.dispatches:read
GET/v1/supported-countries/List supported countries.countries:read

Plan & usage

GET/v1/usage/Plan, limits, and today's per-app usage. Never counts against the quota.
GET/v1/pricing-plans/Public list of available pricing plans.
GET/v1/subscriptions/Your current billing subscriptions.billing:read

Requests & responses

Send JSON request bodies with a Content-Type: application/json header. Every response is wrapped in a consistent envelope so clients can branch on erc before reading the payload:

erc

1 on success, 0 on error.

msg

Human-readable message (success on 2xx, error description on 4xx/5xx).

data

The actual payload — object for retrieves, array for lists.

meta.usage

Present on every developer-token-authenticated success — your plan, daily limit, and remaining quota (see Quotas & usage).

Request

POST /v1/alert-destinations/
POST /v1/alert-destinations/

{
  "label": "Ops channel",
  "channel": "email",
  "identifier": "ops@example.org"
}

Response · 201 Created

json
{
  "erc": 1,
  "msg": "success",
  "total": 1,
  "next": null,
  "data": {
    "id": "d_8fa2",
    "label": "Ops channel",
    "status": "pending"
  },
  "meta": { "usage": {} }
}

Pagination

List endpoints are paginated. Pass page and page_size as query parameters. The envelope's top-level total and next fields give you the page count + cursor; the array of rows lives in data.

GET /v1/alert-dispatches/
GET /v1/alert-dispatches/?page=2&page_size=50

{
  "erc": 1,
  "msg": "success",
  "total": 142,
  "next": "…/v1/alert-dispatches/?page=3",
  "data": [ … ]
}

Quotas & usage

Civic Sentinel meters two things per plan: per-app API requests (counted from 00:00 UTC) and alerts delivered per day. Plans also cap how many applications and WhatsApp destinations you can register.

PlanAppsWhatsAppAPI / app / dayAlerts / day
Free· 0 Coins11105
Signal· 10 Coins / mo553030
Command· 15 Coins / mo775070

There are three ways to read your live quota — pick whichever fits your integration. All three return the same numbers.

1. Response headers

Every developer-token response carries the snapshot in standard rate-limit headers. X-RateLimit-Reset is epoch seconds for the next 00:00 UTC, when counters zero out.

X-PlansignalActive plan slug.
X-RateLimit-Limit30Requests allowed per app per day.
X-RateLimit-Used8Requests recorded so far today (includes this one).
X-RateLimit-Remaining22Requests left before throttling.
X-RateLimit-Reset1747353600Epoch seconds — when the counter resets.
Retry-After27432On 429 only — seconds to wait.

All five X-* headers are listed in Access-Control-Expose-Headers so SDKs and integrations can read them.

2. meta.usage in the response envelope

The same numbers, in JSON, on every successful developer-token response — useful when you can't (or don't want to) inspect headers.

json
{
  "erc": 1,
  "msg": "success",
  "data": {},
  "meta": {
    "usage": {
      "plan": "signal",
      "plan_name": "Signal",
      "application_id": "a22c247a-…",
      "application_name": "Election Insights",
      "limit_per_day": 30,
      "used_today": 8,
      "remaining_today": 22,
      "resets_at": "2026-05-17T00:00:00+00:00"
    }
  }
}

3. GET /v1/usage/

Poll this whenever you want a snapshot without making any other call. This endpoint is exempt from the daily quota — you can hit it even while throttled.

curl
curl https://api.civicsentinel.io/v1/usage/ \
  -H "Authorization: Bearer $CIVIC_SENTINEL_TOKEN"

# response.data
{
  "plan": { "slug": "signal", "name": "Signal", "price_coins": 10 },
  "limits": { "max_apps": 5, "max_api_requests_per_app_per_day": 30, … },
  "today": { "alerts_used": 3, "resets_at": "2026-05-17T00:00:00+00:00"},
  "applications": [ {} ]
}

When you hit the cap

The API responds with 429 Too Many Requests and a populated Retry-After header (seconds until 00:00 UTC). Back off until then, or upgrade your plan.

http
HTTP/1.1 429 Too Many Requests
Retry-After: 27432

{ "erc": 0, "msg": "Daily API quota for application … has been reached. Upgrade your plan or wait until tomorrow (UTC)." }

Capacity caps (creating more apps or WhatsApp destinations than your plan allows) return 403 instead — they aren't time-based, so retrying won't help; you need to upgrade.

Errors

Standard HTTP status codes. Error bodies use the same envelope with erc: 0 and a human-readable msg — either a string or, on validation errors, a { field: "…" } map.

json
{ "erc": 0, "msg": "Invalid API credentials." }
400Bad RequestThe request body or query parameters were invalid.
401UnauthorizedMissing, invalid, or expired bearer token; or invalid token-exchange credentials.
403ForbiddenCredentials are valid but lack the required scope, or your plan does not allow the action.
404Not FoundThe requested resource does not exist.
429Too Many RequestsDaily per-app quota exhausted. Read X-RateLimit-Reset / Retry-After and try again later.
500Server ErrorSomething went wrong on our side. Retry with backoff.

Versioning

The API is versioned in the URL path. Additive changes — new fields, new endpoints, new envelope keys like meta.usage — ship without a version bump, so always code defensively against unknown fields. Breaking changes ship as a new version (for example /v2) and the previous version stays supported during a documented migration window.

Current stable version: v1

Ready to issue your first key?

Open the Developer tab in your console to get started.

Open console