Civesto Authority API
The same interface the authority console uses: read the reports in your jurisdiction, record outcomes, pull aggregates, export in the formats your systems consume, and receive events by webhook. Base URL https://civesto.com/v1/kurumsal. Every response is JSON unless it is a file.
Quickstart
Three calls tell you whether the integration works: who you are, what is waiting, and one case in full.
- Obtain a key from your authority's IT contact (created in the console under API keys) or from [email protected] for a pilot. Keys look like
civ_live_prod_…and are shown once. - Call
GET /me— it returns your authority, your scopes and your role. - Call
GET /ihbarlar?limit=20— the newest reports in your jurisdiction — thenGET /ihbarlar/{id}for one of them.
# who am I, what may I do curl https://civesto.com/v1/kurumsal/me \ -H "X-Civesto-Key: civ_live_prod_REPLACE_ME" # newest reports in my jurisdiction (cursor pagination) curl "https://civesto.com/v1/kurumsal/ihbarlar?limit=20&durum=new" \ -H "X-Civesto-Key: civ_live_prod_REPLACE_ME" # record an outcome (needs the case_write scope) curl -X POST https://civesto.com/v1/kurumsal/ihbarlar/211/status \ -H "X-Civesto-Key: civ_live_prod_REPLACE_ME" \ -H "Content-Type: application/json" \ -d '{"durum": "validated", "not": "Legal basis: SRTI Art. 3(b) — confirmed on site"}'
import requests BASE = "https://civesto.com/v1/kurumsal" H = {"X-Civesto-Key": "civ_live_prod_REPLACE_ME"} me = requests.get(BASE + "/me", headers=H, timeout=10).json() print(me["tenant"]["ad"], me["role"], me["scopes"]) page = requests.get(BASE + "/ihbarlar", headers=H, params={"limit": 20, "durum": "new"}, timeout=10).json() for r in page["items"]: print(r["id"], r["referans"], r["ihlal_turu"], r["durum"], r["olusturulma_zamani"]) # page["next_cursor"] -> pass as ?cursor= for the next page
using System.Net.Http; using System.Net.Http.Json; var client = new HttpClient { BaseAddress = new Uri("https://civesto.com/v1/kurumsal/") }; client.DefaultRequestHeaders.Add("X-Civesto-Key", "civ_live_prod_REPLACE_ME"); var me = await client.GetFromJsonAsync<JsonElement>("me"); var page = await client.GetFromJsonAsync<JsonElement>("ihbarlar?limit=20&durum=new"); foreach (var r in page.GetProperty("items").EnumerateArray()) Console.WriteLine($"{r.GetProperty("id")} {r.GetProperty("ihlal_turu")} {r.GetProperty("durum")}");
const BASE = "https://civesto.com/v1/kurumsal"; const H = { "X-Civesto-Key": "civ_live_prod_REPLACE_ME" }; const me = await (await fetch(`${BASE}/me`, { headers: H })).json(); const page = await (await fetch(`${BASE}/ihbarlar?limit=20&durum=new`, { headers: H })).json(); for (const r of page.items) console.log(r.id, r.referans, r.ihlal_turu, r.durum);
Authentication & roles
One header. The key identifies the authority (tenant) and carries the scopes; nothing else is needed. Keys are stored as Argon2id hashes — the plain value exists only where you keep it.
Headers
| Header | Required | Description |
|---|---|---|
X-Civesto-Key | Yes | The API key (civ_live_…). Authorization: Bearer <key> is accepted as an alternative. |
Accept-Language | No | RFC 5646 tag; validation messages follow it where a translation exists, otherwise English. |
Content-Type | POST/PATCH | application/json. |
Scopes
A key carries a set of scopes. Each endpoint states what it needs; a call without it returns 403 with the missing scope named in details. A key can never create another key with more than it holds itself.
| Scope | Grants |
|---|---|
read | Reports in the jurisdiction: list, detail, evidence media, map data, summary. |
export | File exports (/ihbarlar/export) and CAP alerts. |
case_write | Record an outcome on a report (/ihbarlar/{id}/status). |
stats_read | Aggregates only — summary, trend, heatmap — with no case-level record. |
audit_read | The authority's own audit log. |
webhook_admin | Create, test, pause and delete webhooks; read deliveries. |
key_admin | List, create and revoke API keys of the authority. |
gov_admin | Tenant administration (also satisfies case_write). |
Role presets
The console creates keys by role; a role is simply a scope set. The console shows only what the key can use.
| Role | Scopes | Who |
|---|---|---|
| Case officer | read export case_write | Handles reports, reads evidence, records outcomes, prints case files. |
| Management view | stats_read | Sees volume, outcomes, response times, hotspots and the activity report — never an individual case. |
| IT / systems | audit_read webhook_admin key_admin | Runs the integration: keys, webhooks, audit — never case content. |
Endpoints
All paths are relative to /v1/kurumsal. Jurisdiction is enforced on every report query: a key sees only the reports of its authority's area, and an id outside it answers 404.
| Method · path | Scope | Purpose |
|---|---|---|
GET /health | — | Liveness. |
GET /me | any | Tenant, key prefix, scopes, role, hourly allowance. |
GET /usage | any | Requests in the last hour and today against the allowance. |
GET /ihbarlar | read | Reports, newest first. Filters: durum, suc_tipi, il, ilce, baslangic, bitis; limit ≤ 500; cursor for the next page. |
GET /ihbarlar/{id} | read | One report with evidence seal, forensic observations, routing decision. |
GET /ihbarlar/{id}/foto?idx=0 | read | Evidence media (image or video) — served only through the key, never by file name. |
POST /ihbarlar/{id}/status | case_write | Record an outcome: new · validated · forwarded · acted · closed, with an optional note. Audit-logged. |
GET /ihbarlar/ozet | read or stats_read | Period summary — counts, outcome mix, response times, backlog age, recurring locations, arrival pattern. |
GET /ihbarlar/agrega | read or stats_read | Counts per hourly · daily · weekly · monthly bucket. |
GET /ihbarlar/heatmap | read or stats_read | Grid-binned counts (anonymised); resolution 1–10, optional bbox. |
POST /ihbarlar/geo | read | Reports inside a polygon or radius. |
GET /ihbarlar/export?fmt=… | export | File export — see Exports. |
GET /ihbarlar/{id}/cap | export | CAP 1.2 alert of the routing decision (road-hazard categories). |
GET /export-jobs/{id} | export | Status of an asynchronous export. |
GET · POST /webhooks, GET · PATCH · DELETE /webhooks/{id} | webhook_admin | Manage subscriptions. |
POST /webhooks/{id}/test, GET /webhooks/{id}/deliveries | webhook_admin | Ping the endpoint; read the delivery log. |
GET · POST /keys, DELETE /keys/{id} | key_admin | List, create (plain key returned once), revoke with a reason. |
GET /audit | audit_read | The authority's audit log; filters since, until, method, status_min. |
GET /openapi.json | — | Machine-readable schema of everything above. |
Reports
A report is what one citizen submitted, plus what the platform could verify about it. The record carries observations, not conclusions: the assessment is the authority's.
| Field | Meaning |
|---|---|
id, referans | Numeric API handle; business reference printed on documents (e.g. RD-DEMO-004). |
ihlal_turu, suc_tipi | Stable category code (e.g. yolda_engel) and its English label. Road-hazard codes carry the SRTI article, (EU) 886/2013. |
durum | Pipeline status: new · under_review · validated · forwarded · acted · closed. |
olusturulma_zamani, islem_tarihi | Server receipt time; last recorded action. Device capture time is not exposed by this field. |
enlem, boylam, mahalle, il, ilce | Position and address text as reported; jurisdiction keys. |
plaka, plaka_hash | Partially masked plate value and a pseudonymous grouping value where present. Masking is not anonymisation; uploaded media can still contain identifying details. |
oncelik, ai_skor | Rule-based dispatch priority; ai_skor is a retained compatibility field, not a validated reliability or authenticity assessment. |
medya_muhur, evidence_seal | SHA-256 fingerprints taken at upload; the report's position in the Ed25519-signed, append-only chain. |
forensics | What the file discloses about itself: capture metadata, encoding traces, Content Credentials (including a declared generative origin), measured road conditions for hazard categories. Severities info · caution · flag; no score. |
dispatch | Proposed recipient roles, dispatch urgency and routing rationale. A queued routing entry does not prove delivery or human acknowledgement. |
foto_url | Path of the protected media endpoint, or null. |
Recording an outcome
POST /ihbarlar/{id}/status
{ "durum": "forwarded", "not": "Legal basis: StVO § 12 Abs. 4 — forwarded to the fine office" }
Status changes are recorded in the audit log. The receiving authority must define the processing purpose and applicable legal basis. If a webhook subscribes to report.updated, the change also queues an event.
Summary & statistics
GET /ihbarlar/ozet?days=30 (or baslangic/bitis) returns the period in aggregate: totals, outcome mix, category and priority breakdown, backlog age (open, new, waiting over 72 h, oldest), response times (median, p90, mean of submission → first action), evidence-sealed share, recurring locations (same condition within ~150 m, with open count and centroid) and a weekday × hour arrival matrix. It contains no case identifier, so a stats_read key may call it; repeat plates (masked) are added only for keys that hold read.
GET /ihbarlar/agrega?granularity=daily gives the volume trend; GET /ihbarlar/heatmap?resolution=6 gives ~700 m grid cells for a map layer. The console's Statistics and Activity report views are built from exactly these three calls.
Exports
GET /ihbarlar/export?fmt=… exports records within the key's jurisdiction. The recipient must validate the format and permitted fields; the presence of an adapter is not a completed integration.
fmt | Output | Typical consumer |
|---|---|---|
csv | UTF-8 with BOM | Excel, LibreOffice |
xlsx | Workbook: summary · detail · statistics | Management reporting |
json | Array of records | Any system |
geojson | RFC 7946 FeatureCollection | QGIS, ArcGIS, PostGIS, web maps |
pdf | One page per report | Files, prosecution bundles |
pdfa3 | PDF/A-3 export candidate; archival validation required | Long-term public records |
xml | Plain record schema | Document management |
datex2 | Generic DATEX II v3.5 test publication, validated against pinned XSDs; datex2_profile=generic-3.5-test. Unsupported categories block the export. National profiles and real publication are not enabled. | Acceptance testing with an agreed receiving system |
CAP: GET /ihbarlar/{id}/cap renders a report's routing decision as a Common Alerting Protocol 1.2 envelope (ETSI TS 103 479 lists CAP as a mandatory NG112 interface). It is offered as an interoperable envelope on request, not as a channel any control room already consumes.
Webhooks
Subscribe an HTTPS endpoint of your case-management system and Civesto pushes events to it; your system stays the system of record. Failed deliveries are retried (1 m → 5 m → 30 m → 2 h → 6 h → 24 h) and every attempt is visible in the delivery log.
Events
| Event | Emitted when |
|---|---|
report.created | A new report in the authority's jurisdiction was accepted. |
report.updated | An outcome was recorded on a report (status change with its note). |
* | Everything, including future event types. |
Verifying a delivery
Each POST carries X-Civesto-Event, X-Civesto-Event-Id (idempotency key — deliveries may repeat), X-Civesto-Timestamp and X-Civesto-Signature: sha256=<hex>, an HMAC-SHA256 of the raw request body with the signing secret shown once at creation.
import hmac, hashlib def verify(raw_body: bytes, header: str, secret: str) -> bool: expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest("sha256=" + expected, header)
Answer 2xx quickly and process asynchronously; anything else is retried. The endpoint must be a public HTTPS address — private and link-local ranges are refused at creation.
Keys & audit
POST /keys with {"isim", "scopes", "ip_allowlist", "expires_at"} returns the plain key exactly once. Give every person, integration and wall display its own key with the smallest role that does the job; revoke with DELETE /keys/{id}?reason=… the moment it is not needed. last_used_at answers "is this key still in use?".
GET /audit lists every call made with the authority's keys — timestamp, action, result, origin address, duration — and is the authority's own record of processing. The console adds a CSV export of it.
Errors & limits
Errors are JSON with a stable code, a readable message and details:
{ "error": "forbidden", "message": "This API key is not permitted to perform that action",
"details": { "required": ["read"], "granted": ["stats_read"] } }
| Status | Meaning |
|---|---|
401 | Key missing, malformed, expired or revoked. |
403 | Key lacks the scope, or the source address is outside the key's allow-list. |
404 | Not in your jurisdiction, or it does not exist — the two are not distinguished. |
409 | Nothing to produce (e.g. a CAP alert for a category without a routing rule). |
422 | Validation — the message names the field. |
429 | Hourly allowance reached; Retry-After is set. |
The allowance is per key and per hour, set per agreement (plan defaults from 1,000 to 1,000,000 requests/hour); GET /usage shows consumption. List responses carry X-Civesto-Record-Count.
OpenAPI reference
The complete, generated schema is at /v1/kurumsal/openapi.json. Click Authorize below to try requests with your own key — every call you make here is audit-logged like any other.