★ New · v1.5 · DATA

Your ServiceNow instance, on the map — and writable.

Extract incident tickets and CMDB assets onto the map via the DLT sidecar, write back create / update / close directly to the Table REST API, and poll on a timer for live updates. The C# client talks to ServiceNow directly — no Python middleware process, no new port allocation, no install-hint surface for the write path. Reads land as standard layers; writes never touch the read-only DuckDB hub.

Incident ticketsCMDB assetsOAuth2 (refresh + password) Write-back create/update/closeOpt-in pollerSSE change feedLocationJoin geometryDPAPI-encrypted creds
How it works

Reads via DLT, writes direct, hub stays read-only

The ServiceNow poller composes a DLT REST-API profile pointing at <instance>/api/now/table and re-runs the pipeline on a timer — one storage contract (the staging DuckDB the hub already ATTACHes read-only). Write-back is a focused C# HTTP client that posts straight to ServiceNow, so the hub never sees a write.

  • Reads — DLT RestApi source, paginated (sysparm_limit, default page 10 000), incremental via sys_updated_on cursor → staging DuckDB → FeatureServer / MVT / OGC layers.
  • WritesPOST /records/{profile}/{table} (create), PATCH .../{table}/{sysId} (update), POST /incidents/{profile}/{sysId}/close (sets state=6 + close notes). Direct-to-source; never through DuckDB.
  • OAuth2 — refresh-token grant preferred (ServiceNow rotates the token), password grant fallback. Access token cached DPAPI-encrypted; auto-refresh within 60 s of expiry; reauth-on-401 with one retry.
  • Poller — opt-in, never auto-starts, Task.Delay-cancelable, idempotent on profile switch. Default interval 60 s (min 5 s). Pushes state on an SSE change feed.
POST /api/servicenow/records/{profile}/incident
// create an incident from the map
curl -X POST http://localhost:5059/api/servicenow/records/prod/incident \
  -H 'Content-Type: application/json' \
  -d '{
    "short_description": "Power outage - Sector 4",
    "urgency": "1",
    "location": "a1b2c3d4e5f6..."
  }'

→ OAuth2 token applied per-request (cached)
→ POST <instance>/api/now/table/incident
→ returns sys_id; hub untouched (read-only)
Geometry mapping

Four ways to put a ticket on the map

Most incidents carry a location reference to a cmn_location record with lat/lon. The default LocationJoin mode resolves that join client-side via DuckDB — so a ticket lights up where the asset actually is.

🔗

LocationJoin

Default. Incident location ref → cmn_location lat/lon, joined client-side. The majority case.

📍

LatLon

Two named columns on the table mapped directly to a point.

🏠

Address

Freeform address geocoded post-pipeline via DltAddressGeocoder → a dlt_<id>_geocoded GPKG point layer.

None

Attribute table only — still in the catalog, just not on the map.

The API surface

Profiles, test, sync, poll, write

GroupEndpointNotes
ProfilesGET / POST / PUT / DELETE /api/servicenow/profilesInstance URL + OAuth2 + tables + geometry + poll interval
TestPOST /api/servicenow/test/{profileId}Probes oauth_token.do + one-row sys_user fetch
SyncPOST /api/servicenow/sync/{profileId}One-shot DLT re-run (full refresh, ~60 s latency)
PollerPOST /api/servicenow/polling/{start,stop}Opt-in timer; Task.Delay-cancelable
EventsGET /api/servicenow/eventsSSE change feed: (profileId, state, rowCount, ts)
WritePOST / PATCH /api/servicenow/records/{profile}/{table}[/{sysId}]Create / update direct to Table API
WritePOST /api/servicenow/incidents/{profile}/{sysId}/closeSets state=6 + close notes

Base URL http://localhost:5059. Profile store at <dataDir>/servicenow/profiles.json (plaintext non-secret + DPAPI secret envelope). Disabled by default (ServiceNow:Enabled=false) — write-capable surface, opt-in only. Documented in SERVICENOW_GUIDE.md.

Phase-1: the poller re-runs the full DLT pipeline (~60 s latency) — "live" means about a minute. A dedicated incremental servicenow.duckdb with a sys_updated_on cursor is the documented Phase-2 upgrade for sub-minute freshness. No write-back audit log in v1.

Your ticket queue, mapped and writable

Incidents and CMDB on the map, writes straight back to ServiceNow — no middleware, no new Python process.