Skip to content
WhisperGraph
Skip navigation

Procedure Reference

The complete calling contract for every stored procedure: how CALL binds its arguments, and the exact YIELD columns of every family.

Published

On this page (16)

Procedure Reference Documentation

The complete calling contract for every stored procedure the engine registers. Each entry gives the argument the procedure takes and the YIELD columns it emits, in the order the engine emits them, so you can write the YIELD clause without guessing. Seven procedures have a page of their own with worked examples; they are listed on Procedures. Several also have a matching tool on the MCP server.

Calling a procedure

CALL runs a procedure in three forms: standalone, with YIELD to pick and filter columns, or once per row when the argument comes from UNWIND, WITH or MATCH. A standalone call with no YIELD returns every column the procedure produces. Full clause syntax is in Syntax & Clauses.

cypher · runnablegraph.whisper.securitySign in to run
CALL whisper.variants("paypal.com")
YIELD variant, method, exists, confidenceLabel
WHERE exists
RETURN variant, method, confidenceLabel
LIMIT 6

cypher · runnablegraph.whisper.securitySign in to run
UNWIND ["google.com", "cloudflare.com"] AS d
CALL whisper.psl.tldPlusOne(d) YIELD apex
RETURN d, apex

Five rules cover nearly every failed call:

  • Quote every argument. CALL whisper.identify(ubuntu.com) is rejected as a bad argument, and an unquoted IPv6 literal is parsed as something else entirely and comes back as a syntax error. Always CALL whisper.identify("ubuntu.com").
  • Argument types matter as much as names. whisper.topAsnsByPrefixCount(10) takes an Integer; whisper.explain.bundle takes one string and rejects a list; whisper.export takes exactly one map.
  • YIELD columns are exact contracts. A column the procedure does not emit is rejected, not ignored, and the message names the valid columns. db.relationshipTypes() yields type, not relationshipType.
  • Multi-shape procedures need named columns. explain and whisper.history change their column set with the indicator, so YIELD * is rejected, and so is a YIELD that mixes shapes. Name columns from one shape, or call the single-shape variant: whisper.explain.bundle, whisper.history.whois, whisper.history.bgp.
  • A URL folds to its host. whisper.identify, whisper.assess, whisper.walk and the history procedures read https://host/path?q=1 as host. whisper.assess, whisper.assessUrl, whisper.identify and whisper.enrich take a single string or a list.

The procedures

Every procedure the engine registers, grouped by what it answers. The YIELD columns are exact contracts: a column the procedure does not emit is rejected, not ignored.

Read coverage before band. 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.explain does not return coverage at all. Full contract: Coverage — what we looked at.

Threat & verdict

ProcedureArgumentYIELD columnsRead it like this
explain(indicator), also whisper.explainone string: IP, hostname, ASN, CIDR, file hash or CVE idindicator, type, available, cached, found, score, level, explanation, factors, sources, breakdown, advisory, verdictScoreMulti-shape; name the columns. sources[] entries carry feedId, weight, firstSeen, lastSeen
whisper.explain.bundle(indicator)one string, never a listverdictOne map column that never shifts with indicator type; reach in with verdict.level, verdict.score, verdict.found, verdict.explanation
whisper.assess(hosts)a list or a single string; a URL folds to its hosthost, label, band, sub_labels, signals, coverage, evidence, verdictScoreRead coverage before band
whisper.assessUrl(urls)a list or a single stringurl, host, path, apex_band, path_band, band, coverage, evidencecoverage describes the path, not the host
whisper.enrich(names)a list or a single stringname, owner, country, asn, band, prevalence, coverageRows are de-duplicated by canonical name, so join back by name, never by position. owner is the network operator of the resolved IP's origin AS: a network attribution, never a threat attribution. prevalence is a popularity rank where lower is more prevalent and null is unranked

History

ProcedureArgumentYIELD columnsRead it like this
whisper.history(indicator)one stringWHOIS columns for a domain; routing columns for an IP, ASN or prefixMulti-shape: YIELD within one shape, or call a variant below
whisper.history.whois(domain)one string; a URL folds to its host and a subdomain to its registrable apexindicator, type, queryTime, createDate, updateDate, expiryDate, registrar, registrant, country, nameServers, cached, registrableDomainA fold adds a whois-parent-fold advisory to the response
whisper.history.bgp(indicator)one string: an IP, ASN or prefixindicator, type, origin, prefix, startTime, endTime, visibility, peersSeing, cachedNote the spelling of peersSeing

Attribution & discovery

ProcedureArgumentYIELD columnsRead it like this
whisper.identify(hosts)a list or a single string; a URL folds to its hosthost, vendor_id, canonical_name, is_canonical, confidence, category, roles, band, host_class, evidenceWho runs the host, not whether it is legitimate. Large batches are rejected, not truncated
whisper.walk(host[, depth, budget])a string, then optional Integershost, no_atlas_match, nearest_known_vendors, siblings, coverage, armscoverage is presence, not a verdict: structural-onlywalk — a whisper.walk value describing atlas adjacency. Not an assess coverage value. Do not gate on it., no-data — coverage: no-data. Not in coverage. This is not a verdict — nothing was looked at. or deadline-hit
whisper.origins(domain[, options])a string, optional map {include_related: true}ip, confidence, methods, asn, asnName, kind, category, truncatedconfidence runs 0.0 to 1.0; passive, nothing touches the target
whisper.resolve(host)one stringhost, a, aaaa, freshest_observation_ms, coverageCurrent A and AAAA records from passive data
whisper.search(token[, options])a string, optional map with types, mode, suffix, limit, timeoutMsquery, kind, name, matchedField, matchType, warning, scoreThe bounded front door for a token you cannot classify
whisper.audit.malformedHostnames(zone)one stringclean, malformed, total, samples, scope, truncatedSplits a zone's children into clean and malformed names
whisper.variants(domain)a string; optional node label or false as the filtervariant, method, exists, confidence, confidenceLabelexists: true means registered, not malicious. Also callable as a function in expression position
whisper.lookupTlsFingerprint(hash)a string: a bare hash or kind:hashindicator, found, kind, hash, category, label, family, vendor, client, trustTier, sourceCount, firstSeen, lastSeen, licensePosturefound: false is a populated row
whisper.lookupTorRelay(ip)a string: an exit IP or a relay fingerprintindicator, found, fingerprint, exitAddresses, exitAddressCount, exitAddressesV6, exitAddressCountV6, source, ingestedAtfound: false is a populated row
whisper.danglingCname(hosts)a string or a listhost, target, target_apex, target_apex_state, observed_atZero rows on a clean host; target_apex_state: UNREGISTERED is the takeover signal

CVE plane

ProcedureArgumentYIELD columnsRead it like this
whisper.cve.byPackage(cpe)one full CPE 2.3 stringcve, band, kev, ransomware, epss, cvss, coverageAlways at least one row. A spec it cannot read, such as a Package URL, returns one row with coverage: "unsupported-spec" and everything else null
whisper.vulnPosture(target)a hostname, an ASN, or a map with cves, packages or cpesopenCveCount, scoredCount, critical, high, medium, low, kevCount, ransomwareCount, maxEpss, maxCvss, priority, coverageAlways exactly one row; read coverage before the counts

Infrastructure & BGP

ProcedureArgumentYIELD columnsRead it like this
whisper.asnThreatDensity(asn)one string, "AS13335"asn, listedIps, announcedIpv4, densityRatio, routedPrefixes, coverageListed addresses against announced space
whisper.asnCountries(n)Integercountry, asnsASN count per registration country
whisper.topAsnsByPrefixCount(n)Integerasn, prefixCountThe networks announcing the most prefixes
whisper.bgpDegreeDistribution()noneinDegree, outDegree, asnCountA histogram: one row per degree pair
whisper.asSet(name)one string, an IRR as-set nameasSetName, memberAsn, sourceRirMembership of an IRR as-set, one row per member ASN

Public Suffix List

ProcedureArgumentYIELD columnsRead it like this
whisper.psl.tldPlusOne(host)one stringapexThe registrable apex (eTLD+1)
whisper.psl.isPublicSuffix(name)one stringresultA single boolean
whisper.psl.affiliation(host)exactly one stringfound, suffix, submitterLogin, submitterOrg, evidenceKind, confidencefound: false is a populated row

Threat-intel snapshot candidates

ProcedureArgumentYIELD columnsRead it like this
whisper.threatIntel.candidateCdnApex(n)Integerapex, subCount, certCount, wildcardCount, isOnPslPrivate, recommendation, computedAtPrecomputed CDN and multi-tenant apex candidates
whisper.threatIntel.candidateMultiTenantApex(n)Integername, nodeId, subCount, threatSources, threatScore, isOnDenyList, recommendation, computedAtZero rows means the snapshot holds no candidates of this class
whisper.threatIntel.candidateSharedHostingIp(n)Integerip, nodeId, hostCount, threatSources, threatScore, isAlreadyMarked, recommendation, computedAtSame

Bulk export

ProcedureArgumentYIELD columnsRead it like this
whisper.export(options)exactly one map {label, limit, cursor}; label is required and is malicious, ambiguous — coverage: ambiguous. In coverage, and the evidence points both ways. or benign-allowlistedhost, label, ip, cidr, asn, url_paths, cert_shas, tls_fingerprints, dns, last_seen, coverage, truncated, supersedes, look_alike_negatives, next_cursorAlways pass limit; page by feeding a row's opaque next_cursor back as cursor

Your own context

ProcedureArgumentYIELD columnsRead it like this
whisper.quota()nonekey, valueOne row per key describing your own service context: who the server takes you for and whether it recognised your key. Ask the key, never a page

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, and it describes the ENGINE, not this API: READ answers a question; WRITE names a procedure that would change something, and that you cannot call from here. The public Cypher endpoint rejects mutating calls, and the MCP server exposes read-only tools only — it has no contribution or feedback tool. There is no route by which a reader of this page writes to the graph.

ProcedureModeWhat it does
db.functionsREADList all available Cypher functions (this procedure)
db.labelsREADList all node labels with row counts
db.proceduresREADList all registered procedures (this procedure)
db.propertyKeysREADList all property keys
db.relationshipTypesREADList all relationship types with source/target labels
db.schemaREADFull schema description (labels + types + counts)
db.schema.nodeTypePropertiesREADPer-node-label property index
db.schema.relTypePropertiesREADPer-relationship-type property index
db.schema.visualizationREADSchema graph for visualization
dbms.componentsREADServer component listing (Neo4j-driver compat) — one row {name='whisper-ng', versions=[<ver>], edition='community'}.
explainREADThreat-assessment explanation for an indicator (IP, hostname, ASN, CIDR). level enumerates {NONE, INFO, LOW, MEDIUM, HIGH, CRITICAL}…
whisper.asSetREADIRR 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.asnCountriesREADASN count per country ((:ASN)-[:HAS_COUNTRY]->(:COUNTRY)); one row {country, asns} ordered by count DESC…
whisper.asnThreatDensityREADPer-ASN threat density — one row {asn, listedIps, announcedIpv4, densityRatio, routedPrefixes…
whisper.assessREADMaliciousness-verdict surface for a list of hosts — one row per host with {host, label, band, sub_labels[], signals[], coverage, evidence[], verdictScore, isThreat…
whisper.assessUrlREADURL-scoped maliciousness-verdict surface for a list of URLs — one row per URL {url, host, path, apex_band, path_band, band, coverage…
whisper.audit.malformedHostnamesREADPer-zone HostnameValidator audit — partitions a bounded CHILD_OF scan into clean/malformed buckets.
whisper.bgpDegreeDistributionREADGlobal BGP AS-adjacency degree DISTRIBUTION — one row per (in,out) degree bucket {inDegree, outDegree, asnCount} (all Long)…
whisper.cve.byPackageREADAffecting-CVE listing for ONE package/cpe — whisper.cve.byPackage(spec) where spec is a cpe:2.3 string, {cpe:'cpe:2.3:...'}, or {name, os, osVersion} (distro key).
whisper.danglingCnameREADHost-anchored dangling-CNAME feed lookup — whisper.danglingCname(host
whisper.enrichREADBatched endpoint enrichment for a list of names — whisper.enrich(name[]) returns ONE order-preserving row per canonical name {name, owner, country, asn, band, prevalence, coverage}…
whisper.explainREADAlias for explain
whisper.explain.bundleREADThreat-assessment as a single {verdict: Map} column (single-shape variant of explain)
whisper.exportREADRead-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.historyREADHistorical WHOIS / BGP data for an indicator (auto-pivot)
whisper.history.bgpREADBGP routing history for IP / ASN / prefix (type-strict, single-shape)
whisper.history.whoisREADDomain WHOIS history (type-strict, single-shape)
whisper.identifyREADHost-first vendor attribution over the GOLD RESOLVES_TO->IPV4->DELEGATED_TO->VENDOR path, with an ORIGIN_AS org-graph long tail.
whisper.lookupTlsFingerprintREADTLS handshake fingerprint lookup — probes all 8 kinds (ja3/ja4/ja4s/ja4h/ja4x/ja4t/ja4tscan/jarm) or accepts a kind:hash composite.
whisper.lookupTorRelayREADTor exit-relay lookup — dual-input (a 40-hex Ed25519 fingerprint OR a single exit IPv4/IPv6 address).
whisper.originsREADDiscover candidate origin IPs behind a CDN, scored by independent evidence
whisper.psl.affiliationREADPSL submitter-affiliation lookup by private suffix or hostname.
whisper.psl.isPublicSuffixREADTrue if the input matches a Public Suffix List entry.
whisper.psl.tldPlusOneREADRegistrable apex (eTLD+1) lookup via the Public Suffix List.
whisper.quotaREADWhere the calling key stands right now. Ask the key, never a page.
whisper.resolveREADRead-only DNS resolution for a single host — whisper.resolve(host).
whisper.searchREADBounded 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.submitWRITEContribute an observation back — an indicator or a corroboration receipt. It is a write, so it needs a signed-in key.
whisper.threatIntel.candidateCdnApexREADTop-K precomputed CDN / multi-tenant CA apex candidates from CT + PSL grouping.
whisper.threatIntel.candidateMultiTenantApexREADTop-K precomputed multi-tenant apex candidates from the threat-intel snapshot.
whisper.threatIntel.candidateSharedHostingIpREADTop-K precomputed shared-hosting IPV4 candidates from the threat-intel snapshot.
whisper.topAsnsByPrefixCountREADTop-N ASNs ordered by announced-prefix count; served O(1) from a precomputed snapshot refreshed at BGP cadence.
whisper.variantsREADLookup variants of a hostname/domain
whisper.versionREADServer version + build time — one row {version, buildTime}.
whisper.vulnPostureREADSBOM/CVE-set posture — whisper.vulnPosture({cves:[...], packages:[{name,version,ecosystem}], cpes:[...], os, osVersion}).
whisper.walkREADStructural-neighborhood fallback for a NOVEL host (whisper.walk(host[, depth[, budget_ms]])).
whisper.watchWRITECreate, list and cancel subscriptions to a query, a verdict or an indicator.

The WRITE rows are engine capabilities, not something you can call here. whisper.submit and whisper.watch are registered by the engine and reported by db.procedures(), which is why they appear in this table. They are not reachable through the MCP server, which exposes read-only tools and refuses write procedures by name, and not through the public Cypher endpoint, which rejects any mutating call. There is no contribution or feedback tool on the MCP surface. Treat this column as a statement about the engine, not an invitation.

Rows generated from CALL db.procedures() YIELD name, signature, description, mode RETURN name, signature, description, mode ORDER BY name against https://graph.whisper.security, fetched 2026-09-20T01:36:24Z.

Read the advisories channel

A successful response can carry a top-level advisories[] array beside columns, rows and statistics. Each entry has a kind, a human message and, where they apply, the queried input and the resolved value. It lives on the response envelope, not in a row, so it survives any YIELD or RETURN projection, and the key is omitted when there is nothing to say: test for its presence rather than expecting an empty array. The kinds you will meet on this surface:

kindEmitted byWhat to do
enrich-semanticswhisper.enrichRead it once. It restates how owner, prevalence and row de-duplication work
whois-parent-foldwhisper.history.whoisqueried was folded to resolved; the WHOIS shown belongs to the registrable parent
explain-verdict-axis-unavailableexplain on an ASNscore and level are placeholders on that row. Read breakdown.reputationScore and breakdown.reputationCategory, and do not compare them with a threat band
explain-score-unavailableexplainThe score column holds no usable value for that row. Read level, explanation and factors[]
origins-all-candidates-withheldwhisper.originsEvery candidate was contextual CDN or shared-provider infrastructure. Re-run with {include_related: true} to see them, labelled with the reason
projection-verdict-omitteda query that projects a node without its verdict fieldsIf the verdict is what you need, project verdictLevel and verdictCoverage, or call whisper.assess

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 age of the listings, and hands back the arithmetic in factors[] with the named feeds in sources[]. Reproducing that by walking LISTED_IN edges 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.bgp() 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.

cypher · runnablegraph.whisper.securitySign in to run
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 as type (not relationshipType) with its source and target labels, and flags an edge that is declared but currently empty. 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"), which collapses the result into a single schema column; db.schema.nodeTypeProperties() and db.schema.relTypeProperties() list the properties on each label and edge type. db.functions() and db.procedures() list the callable surface itself. The full label, edge, and property model is on the Graph Schema pages.

ProcedureArgumentYIELD columns
db.labels()nonelabel, count
db.relationshipTypes()nonetype, count, sourceLabels, targetLabels, aliasOf, declaredButEmpty, sparseSourceLabels
db.propertyKeys()nonepropertyKey
db.schema()none, or one of "json", "markdown", "details"without an argument type, name, count, description, example, sourceLabels, targetLabels, fastPatterns, slowPatterns, bestPractices; with a format, a single schema column
db.schema.nodeTypeProperties()nonenodeType, nodeLabels, propertyName, propertyTypes, mandatory
db.schema.relTypeProperties()nonerelType, propertyName, propertyTypes, mandatory
db.schema.visualization()noneschema
db.functions()nonename, signature, description, category
db.procedures()nonename, signature, description, mode
dbms.components()nonename, versions, edition
whisper.version()noneversion, buildTime