ServiceNow tracker adapter¶
Bernstein can pull work directly from a ServiceNow tenant, post progress to the underlying record's work_notes field, and move the record through its state field when a task transitions. The adapter targets the incident table by default. change_request, problem, and custom-application tables are supported via the table_name config field.
The adapter implements bernstein.core.trackers.AbstractTrackerAdapter and lives at bernstein.core.trackers.servicenow.ServiceNowTracker. It is disabled by default and must be opted in via bernstein.yaml.
Auth¶
Basic auth with three environment variables. This is the most common path for ServiceNow Table API tenants. OAuth client-credentials is a future extension.
| Environment variable | Purpose |
|---|---|
SERVICENOW_INSTANCE_URL | Base URL of the tenant, e.g. https://dev12345.service-now.com. |
SERVICENOW_USERNAME | Basic-auth username. |
SERVICENOW_PASSWORD | Basic-auth password. |
Override the env var names per tenant via username_env and password_env on ServiceNowConfig.
Configuration¶
trackers:
servicenow:
instance_url: https://dev12345.service-now.com
table_name: incident
state_field: state
open_query: "active=true"
state_map:
todo: "1"
in_progress: "2"
done: "6"
| Field | Default | Purpose |
|---|---|---|
instance_url | env var | Tenant base URL. |
table_name | incident | Table to operate against. |
state_field | state | Workflow state field. |
open_query | active=true | Default sysparm_query clause. |
state_map | {} | Canonical -> tenant state mapping. |
page_size | 50 | sysparm_limit page size. |
rate_limit_min_interval | 0.0 | Min seconds between calls. |
Pulling, commenting, transitioning¶
from bernstein.core.trackers import ServiceNowConfig, ServiceNowTracker
config = ServiceNowConfig(
instance_url="https://dev12345.service-now.com",
table_name="incident",
state_map={"done": "6"},
)
with ServiceNowTracker(config) as tracker:
for ticket in tracker.pull_open_tickets():
tracker.add_comment(ticket.id, "Bernstein picked this up.")
tracker.transition(ticket.id, "done")
pull_open_tickets(filter)acceptssysparm_queryandfieldsoverrides via the filter dict.add_comment(ticket_id, body)appends towork_notes. Theticket_idis the record'ssys_id.transition(ticket_id, status_id)updates the state field. The value is resolved throughstate_mapfirst, then passed to ServiceNow as-is.
Rate limiting¶
- 429 responses surface as
RateLimited(retry_after=...). The hint is taken from theRetry-Afterheader. - A cooperative per-adapter min-interval throttle is available via
rate_limit_min_interval.
Exceptions¶
| Exception | When |
|---|---|
TrackerUnavailable | 5xx, 401/403, missing config, malformed JSON. |
RateLimited | 429 with optional Retry-After. |
OptimisticConcurrencyError | 412 Precondition Failed on a write with an etag. |
Out of scope¶
- OAuth client-credentials auth (future extension).
- Custom-application schema discovery (operator declares the table and state field).
- Multi-instance routing inside a single adapter instance.