Cookbook

Copy-paste recipes

One goal per recipe, minimal prose, maximum copy-paste. Each assumes you've run Get started (a running server at $V, a $TENANT, an org handle) and links to the full treatment when you want the why. Principal tokens like 7/11 come from POST /v1/admin/principals; 1 is the org-wide token minted by verity-cli dev.

Scope an agent to one account

Scope an agent to one account — the everyday case: an agent that answers only about Acme

bash
curl -s -X POST "$V/v1/scopes" -H 'content-type: application/json' -d '{
  "tenant_id":"'"$TENANT"'",
  "principals":[7,11],
  "entity_scope":["account:acme"],
  "actor_azp":"agent:sales-bot" }'
# → { "scope_handle":"vs_9f2…", "expires_at":"…" }  — pass this to every read/write
Entity scope is what separates two agents holding identical tokens. Full model: Permissions → entity scope.

Scope to a team's accounts

Scope to a team's accounts — a team-bot that spans several accounts it's granted

bash
curl -s -X POST "$V/v1/scopes" -H 'content-type: application/json' -d '{
  "tenant_id":"'"$TENANT"'",
  "principals":[11],
  "entity_scope":["account:acme","account:beta","account:gamma"],
  "actor_azp":"agent:team-bot" }'
# principals:[11] is the group:sales token — it grants the team's ACL reach
A multi-entity scope reads any of its entities; multi-entity content still needs every tag. See Permissions → entity intersection.

Give an agent org-wide read

Give an agent org-wide read — analytics over everything the org token can see

bash
curl -s -X POST "$V/v1/scopes" -H 'content-type: application/json' -d '{
  "tenant_id":"'"$TENANT"'",
  "principals":[1],
  "entity_scope":[],
  "actor_azp":"agent:analytics" }'
# entity_scope:[] is unbound — no entity filter, but visibility still applies.
# "org-wide" = every entity the org token can see, NOT everything. Zero-tag
# content is retrievable here, and only here.
Unbound still enforces visibility. Details: Permissions → recipes.

Make a memory restricted

Make a memory restricted — pricing, quotes, negotiation terms: the tightest class

Confidentiality rides on the write: ingest under a handle whose ceiling reaches restricted. To read it back, the reading handle's ceiling must reach it and the mandatory live recheck must pass.

bash
# a handle that can reach restricted (default ceiling is "internal")
curl -s -X POST "$V/v1/scopes" -H 'content-type: application/json' -d '{
  "tenant_id":"'"$TENANT"'",
  "principals":[7],
  "entity_scope":["account:acme"],
  "max_confidentiality":"restricted" }'
# in dev (no ReBAC), restricted hits are DROPPED unless you opt in explicitly:
#   export VERITY_ALLOW_RESTRICTED_WITHOUT_REBAC=1
The live-recheck contract and the four classes: Permissions → confidentiality and restricted recheck.

Drop in a file (60 seconds)

Drop in a file — the fastest way to put real content in memory

sh
verity-cli add ./handbook --visibility 1                            # a whole directory, org-wide
verity-cli add onboarding.md --visibility 7,11 --entity account:acme  # narrow + entity tag
echo "quick note" | verity-cli add - --visibility 1                     # stdin
verity-cli query "something in it"                                  # verify it landed
--visibility is required and never guessed. Every door and trade-off: Ingestion → add.

Ingest a HubSpot account

Ingest a HubSpot account — a native truth-lane connector, BYOT

Create a private app in your own HubSpot portal (Settings → Integrations → Private Apps), grant CRM read scopes, copy its pat-na1-… token. HubSpot has no per-record ACL, so --visibility is required (tier C, admin-assigned).

sh
export HUBSPOT_PRIVATE_APP_TOKEN=pat-na1-...
export VERITY_URL=http://127.0.0.1:7717
export VERITY_TENANT_ID=$TENANT

# one full backfill cycle (contacts, companies, deals) → keyed L1 facts
python -m verity_ingest.connectors.hubspot --once --visibility 1,2
# poll: 148 fact event(s), cursor -> 2026-07-09T18:04:57.000Z -> {written:148,...}
The full runbook — credential, verify scoped, turn on sync — is Connecting → HubSpot. Salesforce and Google Drive follow the same five steps.

Turn an internal system into a source

Turn an internal system into a source — anything that can POST JSON, in ~60 seconds, no connector code

sh
# 1) mint a scoped webhook URL — visibility is bound into the URL, never widened by a payload
verity-cli webhook mint ops-tool --visibility 1
# → prints a URL like /wh/n8sQ…raw-token   (the token is the credential, shown once)
bash
# 2) POST the native payload — no auth header; the path token authorizes it
curl -s -X POST "$V/wh/n8sQ…raw-token" -H 'content-type: application/json' \
  -d '{"content":"Acme signed the pilot agreement for the fall rollout.",
       "entities":["account:acme"]}'
# → 200 { "episode_id":"…", "chunks_indexed":1, "facts_written":0 }
# unknown shapes → 202 {"quarantined":true} — preserved for review, never indexed permissively
Provenance is admin-assigned; revoke instantly with DELETE /v1/webhooks/{id}. For a reviewable structured mapping, graduate to a source manifest. Full runbook: Connecting → any internal system.

Connect an agent over MCP

Connect an agent over MCP — give Claude Code / Cursor permission-aware memory

sh
verity-cli mcp install            # prints the exact `claude mcp add verity …` line
verity-cli mcp install --run      # …or run it for you

The printed command bakes identity into the environment — never a tool argument:

sh
claude mcp add verity \
  -e VERITY_URL=http://127.0.0.1:7717 \
  -e VERITY_TENANT_ID=$TENANT \
  -e VERITY_PRINCIPALS=1 \
  -e VERITY_ACTOR_SUB=user:me \
  -e VERITY_ACTOR_AZP=agent:claude-code \
  -- /path/to/verity/target/release/verity-mcp
The agent calls memory_open_scope once, then passes the handle to everything else. All 14 tools: API reference → MCP tools.

Propose cross-customer knowledge

Propose cross-customer knowledge — a de-identified generalization, never a customer's specifics

bash
# over REST (or the memory_propose_learning MCP tool). Statement is about a
# CATEGORY, never a customer — a known entity identifier gets quarantined, not published.
curl -s -X POST "$V/v1/knowledge" -H 'content-type: application/json' -d "{
  \"scope_handle\":\"$ACME\",
  \"statement\":\"Healthcare-segment customers require DPA redlines before security review; budget ~2 extra weeks.\",
  \"categories\":[\"industry:healthcare\",\"objection:dpa\"],
  \"evidence\":[\"ep-8f1c…\"]}"
# → { "status":"candidate", "knowledge_id":"k-3f1…" }  — visible only in audit,
#   unpublishable alone. Support accrues from OTHER customers' agents proposing the same.
Publishing needs ≥3 distinct customers and a human gate (off by default). The whole lifecycle: Knowledge layer → a lifecycle.
next These recipes are the fast path. For the reasoning behind each — and the edge cases the copy-paste hides — follow the link under each recipe, or start with Permissions, the page the whole guarantee lives on.