โ˜… 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 built-in 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 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.
  • Writes - POST /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.