Managing Routing Logs
This page covers retrieving and managing the forensic routing log. Read How Routing Logs Work first for what a record contains — the commands below assume you know that a record is one router walk and that the trace is its full story.
Conventions as usual: $VSS, $TOKEN, $NSID; vsscli commands live
under vsscli route-logs … (alias rl). Everything here is
namespace-scoped — unlike CDRs there are no partition-wide variants
(use --include-children from a parent namespace to cover a subtree).
Range and pagination work exactly as for CDRs: --from/--to
(RFC3339 or unix seconds), --since (24h, 7d), default last 24
hours; opaque cursor, --limit, --all.
Permissions
Section titled “Permissions”| Action | Held by |
|---|---|
route_log:read |
partition-admin, namespace-admin, operator, editor, viewer, system-auditor, noc-viewer, cdr-compliance |
route_log:purge |
partition-admin, cdr-compliance only |
noc-viewer is the role built for this data: routing forensics plus
read-only running config — everything a call-routing investigation
needs, nothing else. Note that billing deliberately has no
routing-log access; the forensic log carries dialed-number history
beyond what invoicing needs.
Listing records
Section titled “Listing records”The list returns envelope summaries (no trace), newest first, with optional AND-filters on the envelope:
# Everything in the last daycurl -s "$VSS/v1/ns/$NSID/route-logs" -H "Authorization: Bearer $TOKEN"
# Only failed walks (no destination found) from the edgecurl -s "$VSS/v1/ns/$NSID/route-logs?matched=false&source=edge" \ -H "Authorization: Bearer $TOKEN"
# Everything that resolved to a treatment this weekcurl -s "$VSS/v1/ns/$NSID/route-logs?from=2026-07-06T00:00:00Z&target_type=treatment" \ -H "Authorization: Bearer $TOKEN"vsscli route-logs list --since 24hvsscli route-logs list --matched=false --source edgevsscli route-logs list --since 7d --target-type treatment --include-childrenThe table shows the routing story per row: source, start context, dialed → final number, matched, and the resolved target.
Filters: source (edge|ingest|reroute), matched
(true|false), target_type (a target kind — line, trunk,
treatment, …). They apply to the scanned envelope, so they narrow the
page contents rather than speeding the scan. An unrecognized source or
target_type value is rejected with a 400 listing the accepted
values — a typo can’t masquerade as “no records”.
Auditing one number — dialed
Section titled “Auditing one number — dialed”“How has this number routed over time?” has its own index, keyed by the
as-dialed number. The match is exact — it is a lookup key, not a
substring, and it matches starting_number (before any rewrites):
curl -s "$VSS/v1/ns/$NSID/route-logs?dialed=19195551234&from=2026-07-01T00:00:00Z" \ -H "Authorization: Bearer $TOKEN"vsscli route-logs list --dialed 19195551234 --since 7dThis is the fastest way to answer “when did routing for this DID change?” — scan its records across the window and watch the target (or the rewrite chain) shift between records.
Fetching the full walk
Section titled “Fetching the full walk”The detail endpoint takes a call ULID (every walk of that call — edge, ingest, reroutes, in order) or a leg UUID (the walks touching that leg), and returns complete records including the trace:
curl -s "$VSS/v1/ns/$NSID/route-logs/01K02X5RJ8Q9Z3F7M6T1V4W8YA" \ -H "Authorization: Bearer $TOKEN"
# Narrow to one record / one source within the callcurl -s "$VSS/v1/ns/$NSID/route-logs/01K02X5RJ8Q9Z3F7M6T1V4W8YA?source=reroute" \ -H "Authorization: Bearer $TOKEN"vsscli route-logs get 01K02X5RJ8Q9Z3F7M6T1V4W8YAThe default table renders each walk as a step ledger — one row per
evaluated block with its condition and outcome, the rewrites indented
beneath the block that fired them, and a final ⇒ RESULT row.
-o plaintext renders the same walk as an indented tree:
● edge · 2026-07-12T14:03:11Z call 01K02X5RJ8Q9Z3F7M6T1V4W8YA dialed: 95551234567 │ ├─ internal │ 10 ✗ exact 0 │ 20 ✓ ast _9XXXXXXXXXX (rewrite-only) │ strip-prefix "9" 95551234567 → 5551234567 │ prepend "+1" 5551234567 → +15551234567 │ 30 ✓ always → translation-context:pstn-out ├─ pstn-out │ 10 ✓ ast _+1XXXXXXXXXX → pstn: └─ ⇒ MATCHED pstn: final +15551234567-o json / -o yaml emit the raw records with the full trace for
tooling. --record-id pinpoints a single record; --source filters by
walk source.
The everyday workflow pairs this with CDRs: find the suspect call
(vsscli cdrs legs --filter …), copy its call_ulid, and
route-logs get it to see exactly why it routed where it did.
Purging routing records
Section titled “Purging routing records”Routing records expire on their own after 90 days;
the purge exists for earlier erasure — a compliance request, or a
number-history scrub. It follows the same rules as the CDR purge: it
requires route_log:purge (partition-admin or cdr-compliance), an
explicit to cutoff (a purge with no to is a 400 — it never
defaults to “now”), and a confirmation body; the 412 echoes the exact
date window it would erase, and the vsscli prompt shows the same window
before asking.
curl -s -X DELETE "$VSS/v1/ns/$NSID/route-logs?from=2026-01-01T00:00:00Z&to=2026-06-01T00:00:00Z" \ -H "Authorization: Bearer $TOKEN" \ -d "{\"confirm_id\":\"$NSID\",\"confirm_timestamp\":$(date +%s)}"# → {"success":true,"records_deleted":48213}vsscli route-logs purge --from 2026-01-01T00:00:00Z --to 2026-06-01T00:00:00ZTroubleshooting
Section titled “Troubleshooting”| Response / symptom | Meaning | Do |
|---|---|---|
404 route log not found |
No record with that id in this namespace | Check the namespace; edge records exist only for calls that reached the router (feature-code dials produce none) |
--dialed finds nothing for a number you can see in records |
The index is an exact match on the as-dialed number | Query the number exactly as the caller dialed it (starting_number), not the rewritten form |
A call has no edge record |
The call entered via the controller fallback, or the walk never ran | Look for an ingest record; a missing trace means no translation walk happened |
400 “must be one of” on a filter |
Unrecognized source / target_type value |
Pick from the accepted values listed in the error |
400 missing range bound |
Purge sent without an explicit to cutoff |
Pass --to / ?to= — purge never defaults its upper bound |
| Records older than ~90 days are gone | Automatic expiry | Expected — export downstream if you need longer history |
412 confirmation required |
Purge sent without (or with a stale) confirmation body | Check the echoed window, re-send with confirm_id = the nsid and a fresh unix timestamp |