Give AI agents (Claude, GPT, custom) access to organizational data — with cryptographic proof that they can never see what you didn't allow.
You define which columns an AI agent can read. The agent gets a bearer token. When it reads data, columns outside its scope are physically impossible to decrypt — the encryption keys are never derived. Every read (success or denied) creates a signed audit receipt.
A tenant admin (or a NoData operator) creates the agent from the console or via API. You choose which columns the agent can read.
/tenant-v2/{TENANT_ID}/agents/new → handle + label + classifications → copy the bearer token (shown once).
bash
curl -X POST https://nodatacapsule.com/api/tenant/{TENANT_ID}/agents \
-H "Cookie: ndc-tenant-token=YOUR_TENANT_SESSION" \
-H "Content-Type: application/json" \
-d '{
"handle": "claude-legal",
"label": "Claude for Legal Team",
"allowed_columns": ["first_name", "last_name"],
"team_filter": "legal",
"allowed_classifications": ["public", "internal"]
}'Response:
json
{
"agent": {
"id": "abc-123",
"handle": "claude-legal",
"status": "active"
},
"scope": {
"columns_allowed": {
"agent_demo_records": ["first_name", "last_name"]
},
"where_filter": {
"agent_demo_records": { "owner_team": "legal" }
},
"classification_allowed": ["public", "internal"]
},
"bearer_token": "ndca-eyJhbGciOi...",
"jti": "credential-uuid"
}The agent sends its bearer token with every read request. The server checks the scope and returns only allowed data.
bash
curl -X POST https://nodatacapsule.com/api/agents/claude-legal/read \
-H "Authorization: Bearer ndca-eyJhbGciOi..." \
-H "Content-Type: application/json" \
-d '{
"table": "agent_demo_records",
"columns": ["first_name", "last_name"],
"limit": 10
}'Status: 200 OK
json
{
"rows": [
{ "first_name": "David", "last_name": "Chen" },
{ "first_name": "Lisa", "last_name": "Vance" },
{ "first_name": "Yael", "last_name": "Levy" }
],
"count": 3,
"audit": { "id": "1b0543ec-...", "chain_index": 0 }
}bash
curl -X POST https://nodatacapsule.com/api/agents/claude-legal/read \
-H "Authorization: Bearer ndca-eyJhbGciOi..." \
-H "Content-Type: application/json" \
-d '{
"table": "agent_demo_records",
"columns": ["first_name", "id_number"]
}'Status: 403 Forbidden
json
{
"error": "scope_violation",
"reason": "columns_outside_scope",
"denied_columns": ["id_number"],
"message": "columns outside scope: id_number. allowed: first_name, last_name",
"audit": { "id": "baa23121-...", "chain_index": 1 }
}python
import requests
NODATA_URL = "https://nodatacapsule.com"
AGENT_TOKEN = "ndca-eyJhbGciOi..."
HANDLE = "claude-legal"
def read_data(columns, table="agent_demo_records", limit=50):
res = requests.post(
f"{NODATA_URL}/api/agents/{HANDLE}/read",
headers={
"Authorization": f"Bearer {AGENT_TOKEN}",
"Content-Type": "application/json",
},
json={"table": table, "columns": columns, "limit": limit},
)
if res.status_code == 403:
denied = res.json().get("denied_columns", [])
raise PermissionError(f"Denied: {denied}")
res.raise_for_status()
return res.json()["rows"]
# Works:
people = read_data(["first_name", "last_name"])
# Raises PermissionError("Denied: ['id_number']"):
people = read_data(["first_name", "id_number"])typescript
const NODATA_URL = "https://nodatacapsule.com";
const AGENT_TOKEN = "ndca-eyJhbGciOi...";
const HANDLE = "claude-legal";
async function readData(columns: string[], table = "agent_demo_records") {
const res = await fetch(`${NODATA_URL}/api/agents/${HANDLE}/read`, {
method: "POST",
headers: {
"Authorization": `Bearer ${AGENT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ table, columns }),
});
if (res.status === 403) {
const { denied_columns } = await res.json();
throw new Error(`Denied: ${denied_columns.join(", ")}`);
}
const { rows } = await res.json();
return rows;
}
// Works:
const people = await readData(["first_name", "last_name"]);
// Throws Error("Denied: id_number"):
const people = await readData(["first_name", "id_number"]);json
{
"name": "read_nodata",
"description": "Read scoped organizational data from NoData. Only columns in your grant are accessible.",
"input_schema": {
"type": "object",
"properties": {
"columns": {
"type": "array",
"items": { "type": "string" },
"description": "Columns to read. If you request a denied column, you'll get a 403."
},
"table": {
"type": "string",
"default": "agent_demo_records"
}
},
"required": ["columns"]
}
}Give this tool definition to Claude/GPT. The LLM calls it with columns it needs. If it asks for a denied column, the API returns 403 and the LLM learns it can't access that data. Every attempt is audited.
bash
# From the console: /tenant-v2/{TENANT_ID}/agents
# From API:
curl https://nodatacapsule.com/api/tenant/{TENANT_ID}/agents \
-H "Cookie: ndc-tenant-token=YOUR_TENANT_SESSION"bash
curl -X DELETE https://nodatacapsule.com/api/tenant/{TENANT_ID}/agents/claude-legal \
-H "Cookie: ndc-tenant-token=YOUR_TENANT_SESSION" \
-H "Content-Type: application/json" \
-d '{"reason": "project completed"}'Token stops working immediately. Agent status → frozen. All future reads → 401. The revocation is recorded in the audit chain.
bash
# Give all agents in the tenant a new scope:
curl -X POST https://nodatacapsule.com/api/tenant/{TENANT_ID}/agents/grants/batch \
-H "Cookie: ndc-tenant-token=YOUR_TENANT_SESSION" \
-H "Content-Type: application/json" \
-d '{
"selection": { "all": true },
"scope": {
"columns_allowed": { "agent_demo_records": ["first_name", "last_name", "email"] },
"where_filter": {},
"classification_allowed": ["public"]
}
}'bash
# Every read (success + denied) is in the audit chain:
# From the console: /tenant-v2/{TENANT_ID}/agents → audit
# From browser: /verify-pack/agent-read/{audit_id}
# Auditor can verify HMAC chain independently — no trust in NoData required| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
| /api/tenant/{id}/agents | POST | Operator | Create agent + mint token |
| /api/agents/{handle}/read | POST | Bearer | Read data (scope enforced) |
| /api/tenant/{id}/agents/{handle} | GET | Operator | Agent detail + audit |
| /api/tenant/{id}/agents/{handle} | DELETE | Operator | Revoke agent |
| /api/tenant/{id}/agents/grants/batch | POST | Operator | Update scope for multiple agents |
| /verify-pack/agent-read/{id} | GET | Public | Verify single audit entry |