Make
Every Make scenario that touches an agent's network identity is a leap of faith — nobody in the automation can say whether the caller is the agent you provisioned or an impostor replaying its address.
Verifying that requires a DANE check, a DNSSEC-validated PTR lookup, and an RDAP fetch — three HTTP modules and a pile of parseJSON gymnastics most builders skip. Provisioning a new agent normally means shelling out to a control API by hand, because no low-code platform speaks Whisper's control verb natively. The Whisper Agent Identity app closes both gaps: four keyless modules that verify an address in one click, and five keyed modules that provision, police, and retire agents inside any scenario.
The app is two tiers, deliberately
Per Whisper's integration standard, the app never forces a choice between "just a verifier" and "full control." It ships as one app, two connection tiers, so a builder gets value immediately and more value the moment they add a key:
| Tier | Connection | Endpoint | Modules |
|---|---|---|---|
| Keyless | none (connection: null) |
https://rdap.whisper.online |
Verify Agent Identity, Lookup RDAP Record, Get Transparency Log, Get Inbound Lookups |
| Keyed | whisper (apikey) |
https://graph.whisper.security/api/query |
Register Agent, Set Policy, Get Logs, Revoke Agent, List |
Drop the app into a scenario with no connection at all and the four keyless modules already work — anyone building an "is this caller legit" branch gets it for free. Add a whisper_live_… key to the Whisper API key connection and the five control modules light up in the same module picker.
Every module maps a request/response straight onto Whisper's public control API — a plain, readable contract you can check before you trust it. Sibling recipes: Zapier, Pipedream, and the full wire contract at CONTROL_API.md.
Mechanism: what "verify" actually checks
Verify Agent Identity calls GET /verify-identity?ip=<address> and returns a structured verdict, not a guess. Under the hood the endpoint runs the same chain a human would run by hand — the module just does it in one request:
- PTR lookup — reverse the
/128underip6.arpaand confirm it resolves to the agent's FQDN. - Forward AAAA — resolve the FQDN back to an address and confirm it matches: forward-confirmed reverse DNS (FCrDNS), which closes the PTR-spoofing loop (IPv6 reverse-DNS per RFC 8501).
- DANE/TLSA — fetch
_443._tcp.<fqdn>TLSA under DNSSEC and require the AD (Authenticated Data) bit, then confirm usage3 1 1(DANE-EE, no public CA in the loop — RFC 6698 §2, RFC 4034 for the DNSSEC signature chain). - Served-leaf match — confirm the certificate actually served on
:443hashes to that TLSA record.
For the public demo agent at 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 (acef2002a323d40d4.<tenant>.agents.whisper.online), that chain resolves the TLSA record 3 1 1 b653a4ef…fcb82d1d. A pass means: this address is a real Whisper agent, its reverse and forward DNS agree, and its certificate is cryptographically pinned by DNSSEC — not merely "some CA signed something."
You could run that chain from a terminal — five tools, four round trips, eyeballing the ad flag and diffing two hashes by hand (it's on Verify an agent). In Make you don't: one module does all of it.
The Verify Agent Identity module
Drop Verify Agent Identity into a scenario, map {{1.ip}} from a webhook trigger to the ip field, and the single module returns:
{
"is_whisper_agent": true,
"fqdn": "acef2002a323d40d4.<tenant>.agents.whisper.online",
"dane_ok": true,
"operator": "<tenant>",
"verified_at": 1782919008167
}
Branch the scenario on dane_ok = true and you have a router that trusts nothing it can't cryptographically verify — zero HTTP modules, zero manual DNSSEC reads.
The keyed half: provisioning as a scenario step
The five keyed modules are thin, deterministic wrappers over the one control verb, whisper.agents({op, args}) — full contract in CONTROL_API.md. Each module builds the Cypher call, POSTs it with the connection's key in X-API-Key, and normalizes whichever response envelope the API returns that day (the live procedure-row shape or the flat dev-guide shape) into plain output fields — so a scenario never has to branch on wire format.
Register Agent, for example, takes label (and optional contact_email) and returns a routable identity in one step:
{"query":"CALL whisper.agents({op:'register', args:{label:'checkout-bot'}})"}
→ agent, address (the new /128), fqdn, ptr, and api_key — the agent's own key, minted once and shown once. A scenario that runs "new customer signs up → provision them an agent → store the key in a data store, never in a log" is five clicks, not a backend deploy.
The other four keyed modules round out the fleet lifecycle: Set Policy (op:'policy', default/block[]/allow[] — the tenant's DNS resolver policy), Get Logs (op:'logs', windowed dns/conn/alloc activity), Revoke Agent (op:'revoke', irreversible), and List (op:'list', the tenant's agents/identities/records).
With stock tools
curl -s https://graph.whisper.security/api/query \
-H "X-API-Key: whisper_live_xxxxxxxxxxxxxxxxxxxx" \
-H 'content-type: application/json' \
-d '{"query":"CALL whisper.agents({op:'\''register'\'', args:{label:'\''checkout-bot'\''}})"}' \
| jq .
You own the quoting (a Cypher string literal, single quotes doubled), the envelope-shape branch (result vs rows[0].result), and never accidentally log api_key.
With Whisper (the Make module, and the CLI for comparison)
In Make: Register Agent → map label from an incoming field → wire {{5.api_key}} straight into a data-store "Add a record" module, never a log or a router field that gets echoed downstream. Equivalent from a terminal, for parity:
whisper agents register --label checkout-bot --json
A worked scenario: gate a webhook on identity, then provision on demand
A realistic flow that uses both tiers in one scenario:
- Webhook — receives
{caller_ip, order_id}from an upstream service. - Verify Agent Identity (keyless) —
ip = {{1.caller_ip}}. - Router — branch on
{{2.dane_ok}} = true. - Not verified → Slack: Post Message, "rejected unverified caller{{1.caller_ip}}", scenario ends. - Verified → continue. - List (keyed,
kind: agents) — confirm{{2.fqdn}}'s agent is stillactive(catches a revoked-but-not-yet-expired DNS cache). - Get Logs (keyed,
kind: conn,from: -1h) — pull the agent's last hour of connection activity for an audit trail alongside the order. - Data store: Add a record — order id + verified identity + log excerpt, no bearer/API key ever touched a field that Make persists in the execution history.
Because step 2 needs no connection at all, a builder can prototype steps 1–3 with zero Whisper account, then add the key only when they reach step 4 — exactly the "verify free, provision with a key" shape the app is built to teach.
404 is not an error here. verifyIdentity and lookupRdap both answer HTTP 404 for "not a Whisper agent" / "no identity" (a legitimate negative, not a broken endpoint) — Make treats all 4xx as module errors, so the app maps that 404 into a specific, readable DataError message. Attach an error handler (or set the module to Resume) on those two modules so "not an agent" is a normal branch, not a scenario crash.
Install it
The app is not yet in the public Make app directory pending review; until then, load the sections into Development → Custom apps in that order (Base, four keyless modules, one apikey connection, five keyed modules), or drive the same layout through Make's SDK Apps API if you prefer to script it.
Next: Zapier for the equivalent two-tier app on Zapier, or Verify for the DANE/DNSSEC chain this app's keyless modules are built on.