Procedures
What WhisperGraph's stored procedures do, how CALL works, and when a procedure beats a hand-written traversal.
On this page (9)
Procedures Documentation
WhisperGraph ships stored procedures you call from Cypher with CALL. Each one wraps a multi-step computation — threat scoring, lookalike generation, WHOIS and BGP history, CDN origin discovery, infrastructure identity — into a single call that returns a clean result set instead of a hand-written traversal. They run on the public API at https://graph.whisper.security/api/query, and several have matching tools on the MCP server.
Scores and verdicts are live reads. The value reflects whatever data is loaded at query time, so treat a result as current, not fixed.
Calling a procedure
CALL runs a procedure standalone, with YIELD to pick and filter columns, or once per row in the UNWIND ... CALL form. A standalone call with no YIELD returns every column the procedure produces. Full clause syntax is in Syntax & Clauses.
CALL whisper.variants("paypal.com")
YIELD variant, method, exists, confidenceLabel
WHERE exists
RETURN variant, method, confidenceLabel
LIMIT 6
The procedures
Four procedures do the heavy lifting, each with its own page. Beyond them, an identity family answers "whose infrastructure is this", and a set of helpers cover naming and lookups.
Read
coveragebeforeband. Only known-clean — coverage: known-clean. In coverage, no malicious evidence. licenses the word "clean"; no-data — coverage: no-data. Not in coverage. This is not a verdict — nothing was looked at. means unknown, which is a different thing again; malicious-evidenced — coverage: malicious-evidenced. In coverage, with positive evidence of malice. and ambiguous — coverage: ambiguous. In coverage, and the evidence points both ways. mean there is evidence, whatever the band says.whisper.explaindoes not returncoverageat all. Full contract: Coverage — what we looked at.
Investigation
| Procedure | What it answers | Notes |
|---|---|---|
explain(indicator) | Threat verdict for an IP, domain, ASN, or CIDR: score, level, factors[], sources[] | Auto-detects the indicator type |
whisper.variants(domain) | Typosquat and lookalike variants, filtered by default to ones registered in the graph | exists: true means registered, not malicious |
whisper.history(indicator) | WHOIS snapshots for a domain; BGP routing history for an IP, ASN, or prefix | WHOIS and routing columns cannot be YIELDed together — one shape per call |
whisper.origins(domain) | Candidate origin IPs behind a CDN or proxy, ranked by confidence | Passive lookup; no packet touches the target |
Identity & assessment
| Procedure | What it answers | Notes |
|---|---|---|
whisper.identify(hosts) | Whose infrastructure a host is — canonical name, category, roles | 1 to 256 hosts per call |
whisper.assess(hosts) | Whether a host is dangerous, qualified by coverage | Gate on coverage: no-data — coverage: no-data. Not in coverage. This is not a verdict — nothing was looked at. ≠ benign, and malicious-evidenced — coverage: malicious-evidenced. In coverage, with positive evidence of malice. fires at low bands |
whisper.walk(host) | A bounded structural neighborhood when identify has no direct match | Depth- and budget-bounded |
Helpers
| Procedure | What it answers | Notes |
|---|---|---|
| PSL functions | Registrable apex and public-suffix tests | whisper.psl.tldPlusOne, whisper.psl.isPublicSuffix |
whisper.topAsnsByPrefixCount | The ASNs announcing the most prefixes | Ranked list |
| Tor / TLS lookups | Whether an IP runs a Tor relay or emits a known TLS fingerprint | whisper.lookupTorRelay, whisper.lookupTlsFingerprint |
whisper.search(token) | A bounded, type-aware lookup of an unclassified token — IP, host, ASN, CIDR, prefix, or suffix | Use it instead of an unanchored scan |
db.labels(), db.relationshipTypes(), db.propertyKeys(), db.schema() | The live schema | Cheap, and they answer immediately |
Every procedure
The tables above are the ones worth learning first. This one is the whole surface, generated from CALL db.procedures() — every procedure the engine registers, whether or not a page has been written about it. Mode is the engine's own: READ answers a question, WRITE changes something and needs a signed-in key.
| Procedure | Mode | What it does |
|---|---|---|
db.labels | READ | List all node labels with row counts |
db.relationshipTypes | READ | List all relationship types with source/target labels |
db.propertyKeys | READ | List all property keys |
db.schema | READ | Full schema description (labels + types + counts) |
db.schema.visualization | READ | Schema graph for visualization |
db.schema.nodeTypeProperties | READ | Per-node-label property index |
db.schema.relTypeProperties | READ | Per-relationship-type property index |
db.functions | READ | List all available Cypher functions (this procedure) |
db.procedures | READ | List all registered procedures (this procedure) |
dbms.components | READ | Server component listing (Neo4j-driver compat) — one row {name='whisper-ng', versions=[<ver>], edition='community'}. |
whisper.version | READ | Server version + build time — one row {version, buildTime}. |
explain | READ | Threat-assessment explanation for an indicator (IP, hostname, ASN, CIDR) |
whisper.explain | READ | Alias for explain |
whisper.explain.bundle | READ | Threat-assessment as a single {verdict: Map} column (single-shape variant of explain) |
whisper.history | READ | Historical WHOIS / BGP data for an indicator (auto-pivot) |
whisper.history.whois | READ | Domain WHOIS history (type-strict, single-shape) |
whisper.history.bgp | READ | BGP routing history for IP / ASN / prefix (type-strict, single-shape) |
whisper.quota | READ | Where the calling key stands right now. Ask the key, never a page. |
whisper.variants | READ | Lookup variants of a hostname/domain |
whisper.origins | READ | Discover candidate origin IPs behind a CDN, scored by independent evidence |
whisper.search | READ | Bounded analyst search — routes an untyped token to an exact index lookup (IPv4/IPv6/CIDR/ASN/hostname/exact ASN-name), a bounded FST prefix scan… |
whisper.lookupTlsFingerprint | READ | TLS handshake fingerprint lookup — probes all 8 kinds (ja3/ja4/ja4s/ja4h/ja4x/ja4t/ja4tscan/jarm) or accepts a kind:hash composite. |
whisper.lookupTorRelay | READ | Tor exit-relay lookup — dual-input (a 40-hex Ed25519 fingerprint OR a single exit IPv4/IPv6 address). |
whisper.psl.tldPlusOne | READ | Registrable apex (eTLD+1) lookup via the Public Suffix List. |
whisper.psl.isPublicSuffix | READ | True if the input matches a Public Suffix List entry. |
whisper.psl.affiliation | READ | PSL submitter-affiliation lookup by private suffix or hostname. |
whisper.threatIntel.candidateMultiTenantApex | READ | Top-K precomputed multi-tenant apex candidates from the threat-intel snapshot. |
whisper.threatIntel.candidateSharedHostingIp | READ | Top-K precomputed shared-hosting IPV4 candidates from the threat-intel snapshot. |
whisper.threatIntel.candidateCdnApex | READ | Top-K precomputed CDN / multi-tenant CA apex candidates from CT + PSL grouping. |
whisper.topAsnsByPrefixCount | READ | Top-N ASNs ordered by announced-prefix count; served O(1) from a precomputed snapshot refreshed at BGP cadence. |
whisper.asnCountries | READ | ASN count per country ((:ASN)-[:HAS_COUNTRY]->(:COUNTRY)); one row {country, asns} ordered by count DESC… |
whisper.asnThreatDensity | READ | Per-ASN threat density — one row {asn, listedIps, announcedIpv4, densityRatio, routedPrefixes… |
whisper.bgpDegreeDistribution | READ | Global BGP AS-adjacency degree DISTRIBUTION — one row per (in,out) degree bucket {inDegree, outDegree, asnCount} (all Long)… |
whisper.audit.malformedHostnames | READ | Per-zone HostnameValidator audit — partitions a bounded CHILD_OF scan into clean/malformed buckets. |
whisper.asSet | READ | IRR as-set MEMBERSHIP lookup (NOT asset management) — one row per member ASN of the named as-set, served from the local IRR snapshot; no upstream call. |
whisper.assess | READ | Maliciousness-verdict surface for a list of hosts — one row per host with {host, label, band, sub_labels[], signals[], coverage… |
whisper.assessUrl | READ | URL-scoped maliciousness-verdict surface for a list of URLs — one row per URL {url, host, path, apex_band, path_band, band, coverage… |
whisper.identify | READ | Host-first vendor attribution over the GOLD RESOLVES_TO->IPV4->DELEGATED_TO->VENDOR path. |
whisper.walk | READ | Structural-neighborhood fallback for a NOVEL host (whisper.walk(host[, depth[, budget_ms]])). |
whisper.resolve | READ | Read-only DNS resolution for a single host — whisper.resolve(host). |
whisper.export | READ | Read-only bulk export of the threat corpus by label (malicious, ambiguous — coverage: ambiguous. In coverage, and the evidence points both ways., benign-allowlisted), for classifier distillation. |
whisper.watch | WRITE | Create, list and cancel subscriptions to a query, a verdict or an indicator. |
whisper.submit | WRITE | Contribute an observation back — an indicator or a corroboration receipt. It is a write, so it needs a signed-in key. |
whisper.agents | WRITE | Agent control-plane front door. Disabled unless a deployment turns it on. |
Rows generated from CALL db.procedures() YIELD name, signature, description, mode against https://graph.whisper.security, fetched 2026-08-09T21:11:53Z.
When to prefer a procedure over a traversal
Reach for the procedures first. They answer the hardest questions in one call, usually faster and cleaner than a hand-written deep traversal.
- The logic runs server-side.
explain()computes a score from feed count, feed weights, recency, and the threat density of the surrounding network, and hands back the arithmetic infactors[]with the named feeds insources[]. Reproducing that by walkingLISTED_INedges yourself takes more hops and gives you less evidence. - A procedure replaces a slow scan. Where a hand-written query walks the graph itself, a procedure does the same work in one call, which makes it the standard fix for a query that runs long — alongside anchoring the query and adding a
LIMIT. See Best Practices. - The output is decision-ready. A procedure returns labeled columns you can paste straight into a ticket.
One caveat: BGP routing history over a large network is slow. Keep a LIMIT on whisper.history() calls and expect a longer round trip.
Access
Some of these calls need an API key. Pass it in the X-API-Key header; sign in to get one — there is no card to enter.
Schema introspection
The db.* procedures describe the live schema, so you can confirm a label or edge exists before you anchor on it.
CALL db.labels() YIELD label RETURN label ORDER BY label LIMIT 12
db.labels() lists every node label with its count. db.relationshipTypes() lists every edge type with its source and target labels. db.propertyKeys() lists every property name in use. db.schema() returns a structured overview of the whole graph and accepts a format argument ('json', 'markdown', or 'details'); db.schema.nodeTypeProperties() and db.schema.relTypeProperties() list the properties on each label and edge type. The full label, edge, and property model is on the Graph Schema pages.