Contents
Quick Start — working in 30 seconds
Three calls: store a fact, prove a property about it without revealing the fact, and verify the proof. Replace YOUR_API_KEY with your key from the dashboard.
JavaScript — store → prove → verifyjavascript
const API = "https://zkshare.io/api/v1/context";
const KEY = "YOUR_API_KEY";
async function zkshare(body) {
const res = await fetch(API, {
method: "POST",
headers: { "x-api-key": KEY, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
return res.json();
}
// 1. Store a fact
const stored = await zkshare({
operation: "store",
user_id: "user_123",
fact_key: "vacation_preference",
value: "strongly prefers beach vacations",
});
console.log("Commitment:", stored.data.commitment);
// 2. Prove a property (server answers yes/no, signs it)
const proved = await zkshare({
operation: "prove",
user_id: "user_123",
fact_key: "vacation_preference",
query: "does the user prefer beach vacations?",
});
console.log("Answer:", proved.data.answer); // "yes"
console.log("Proof:", proved.proof);
// 3. Verify the proof (no plaintext involved)
const verified = await zkshare({
operation: "verify_proof",
proof: proved.proof,
});
console.log("Valid:", verified.data.valid); // trueAgent Integration
ZKshare works as a tool for any AI agent framework. Register it as a function the agent can call — the agent decides when to store, prove, or search based on user intent. The example below uses OpenAI function-calling, but the pattern is the same for LangChain, CrewAI, Vercel AI SDK, or any framework that supports tool/function definitions: describe the operations as a JSON schema, and forward calls to the single POST endpoint. Your agent only needs the API key. No SDKs, no extra dependencies.
OpenAI Function-Calling Examplejavascript
// OpenAI function-calling — give your agent a "zkshare" tool
import OpenAI from "openai";
const openai = new OpenAI();
const ZK_API = "https://zkshare.io/api/v1/context";
const ZK_KEY = "YOUR_API_KEY";
// Define ZKshare as a tool the agent can call
const tools = [
{
type: "function",
function: {
name: "zkshare",
description:
"Privacy API. Store encrypted facts, prove properties " +
"without revealing data, share answers, or search.",
parameters: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["store", "prove", "share", "search", "verify_proof", "sandbox"],
},
user_id: { type: "string" },
fact_key: { type: "string" },
value: { type: "string" },
query: { type: "string" },
},
required: ["operation"],
},
},
},
];
// When the model calls the tool, forward to ZKshare
async function handleToolCall(call) {
const args = JSON.parse(call.function.arguments);
const res = await fetch(ZK_API, {
method: "POST",
headers: { "x-api-key": ZK_KEY, "Content-Type": "application/json" },
body: JSON.stringify(args),
});
return res.json();
}
// Run the agent
const response = await openai.chat.completions.create({
model: "gpt-4o",
tools,
messages: [
{ role: "system", content: "You have access to a privacy API via zkshare." },
{ role: "user", content: "Store that I prefer beach vacations, then prove it." },
],
});
// The agent will call zkshare("store") then zkshare("prove") automaticallyOverview
ZKshare is a privacy-oriented context API for users, agents, and back-office systems. A single HTTP entrypoint exposes six operations: - store — server-sealed (AES-GCM) or client-sealed (E2EE) facts - prove — return a signed yes/no envelope bound to a commitment - share — same as prove, plus a single-use, time-bound share token - search — semantic search over server-sealed facts (pgvector) - verify_proof — validate a previously issued envelope without loading plaintext - sandbox — execute a small allow-listed action inside an isolated VM Server-sealed facts: the server holds the AES-256-GCM key (`ZKSHARE_ENCRYPTION_SECRET`) and decrypts in memory only when you call prove, share, or generate a search summary. Client-sealed facts: the server only sees ciphertext, IV, auth tag, commitment, and your supplied 1536-dim embedding. Client-sealed rows are excluded from server-side search, prove, and share — use local crypto plus verify_proof. Today’s proof field is a versioned JSON envelope signed with HMAC-SHA256 (commitment, query, answer, nonce). `snarkjs` is included for future Groth16 wiring; that path is not on the default response trust model yet.
Authentication
All API requests require an API key passed in the `x-api-key` header. End users and agents only need this key — no LLM provider account, no Supabase login, no sandbox keys. Those are operator-side concerns (see "What you bring vs. what we run" below).
curl -X POST https://zkshare.io/api/v1/context \
-H "x-api-key: zk_live_abc123" \
-H "Content-Type: application/json"Main Endpoint
All operations go through a single endpoint: `POST /api/v1/context` The `operation` field in the request body determines the action.
What you bring vs. what we run
End user / agent — only needs the ZKshare API key (`x-api-key: zk_live_…`). No other credentials, no LLM account, no extra SDK. Operator (the team running this API) — configures cryptographic secrets, the database, and optionally an LLM provider used internally for two things: - generating 1536-dim embeddings for semantic search - deciding yes/no answers to natural-language predicates inside prove and share The end user never sees an LLM key, never sends one, and never needs one. If the operator sets `ZKSHARE_DISABLE_EXTERNAL_LLM=true`, no third-party LLM is contacted at all; prove/share answers fall back to a deterministic heuristic and search summaries are static. This is the most private mode but also the least intelligent. For client-sealed (E2EE) stores, no LLM ever touches your data: the server only stores the ciphertext, IV, auth tag, commitment, and the embedding you supply. Use this mode when you do not want any third party to see plaintext.
Trust model and the ZK claim
Be exact about what is verifiable today vs. what is on the roadmap. Today (v1.0): - AES-256-GCM encryption at rest. The operator holds the key for server-sealed rows; the client holds the key for client-sealed rows. - HMAC-SHA256 proof envelopes binding (commitment, query, answer, nonce, expires_at). `verify_proof` checks the signature without loading plaintext. - Deterministic salted SHA-256 commitments per (user_id, fact_key, value). - Sandboxed action execution via `node:vm` with a signed HS256 JWT attestation. Every response advertises `provider: "vm-sandbox"` so callers know this is software isolation, not hardware. Roadmap: - Groth16 SNARKs for structured predicates (equality, range, set membership, commitment-knowledge). Natural-language predicates cannot be SNARK-proven and will continue to use HMAC envelopes — the response will gain a `proof_type` field so callers can require `groth16` when they need it. - Real TEE provider (AWS Nitro Enclaves or equivalent) replacing the vm-sandbox. The response shape (`attestation.provider`, `proof_of_execution`) is stable across that swap. Treat any external claim of "full SNARK-on-every-call" or "hardware-attested enclave" as aspirational unless the verifier and circuit artifacts have been published and audited. The current implementation is honestly described in every response.
Operations
storeStore an encrypted fact. Server-sealed: send value. Client-sealed: send ciphertext + iv + auth_tag + commitment + required 1536-dim embedding (no plaintext on the wire).Requestjson
{
"operation": "store",
"user_id": "user_123",
"fact_key": "vacation_preference",
"value": "strongly prefers beach vacations over mountains"
}Responsejson
{
"success": true,
"operation": "store",
"data": { "fact_id": "uuid", "commitment": "0x…", "client_encrypted": false },
"proof": "zkshare:v1+hmac;…",
"verified": true,
"timestamp": "2026-04-28T12:00:00Z",
"usage": { "calls": 42, "limit": 1000 }
}proveReturn a signed yes/no envelope bound to the fact commitment and the query. Today the envelope is HMAC-SHA256; SNARK verification is on the roadmap.Requestjson
{
"operation": "prove",
"user_id": "user_123",
"fact_key": "vacation_preference",
"query": "does the user prefer beach vacations?"
}Responsejson
{
"success": true,
"operation": "prove",
"data": { "answer": "yes", "commitment": "0x…" },
"proof": "base64url-proof-payload…",
"verified": true,
"timestamp": "2026-04-28T12:00:00Z",
"usage": { "calls": 43, "limit": 1000 }
}verify_proofVerify an HMAC-sealed proof from prove/share without loading fact plaintext. Malformed proof strings return 400; valid envelope with bad signature returns 200 with data.valid false.Requestjson
{
"operation": "verify_proof",
"proof": "base64url-envelope-from-prove-or-share…"
}Responsejson
{
"success": true,
"operation": "verify_proof",
"data": { "valid": true },
"proof": "…same proof echoed…",
"verified": true,
"timestamp": "2026-04-28T12:00:00Z",
"usage": { "calls": 47, "limit": 1000 }
}searchSemantic search over server-sealed facts only. Client-sealed rows are not ranked or summarized on the server.Requestjson
{
"operation": "search",
"user_id": "user_123",
"query": "what does the user like for holidays?"
}Responsejson
{
"success": true,
"operation": "search",
"data": {
"results": [
{ "fact_key": "vacation_preference", "relevance": 0.92, "answer": "…" }
]
},
"proof": null,
"verified": true,
"timestamp": "2026-04-28T12:00:00Z",
"usage": { "calls": 45, "limit": 1000 }
}sandboxRun a small allow-listed action inside an isolated node:vm sandbox (no host I/O, 50 ms timeout). Returns the result with attestation metadata and a short-lived HS256 JWT. Every response advertises provider: vm-sandbox — this is software isolation, not hardware attestation.Requestjson
{
"operation": "sandbox",
"user_id": "user_123",
"action": "calculate_travel_budget",
"parameters": {
"monthly_income": 6500,
"current_savings": 12000,
"preferred_destination": "beach"
}
}Responsejson
{
"success": true,
"operation": "sandbox",
"data": {
"result": { "recommended_budget": 4500, "affordable": true },
"attestation": {
"sandbox_id": "zkshare-sandbox-v1-…",
"measurement": "sha256:…",
"provider": "vm-sandbox",
"verified": true
}
},
"proof": "jwt-proof-of-execution…",
"verified": true,
"timestamp": "2026-04-28T12:00:00Z",
"usage": { "calls": 46, "limit": 1000 }
}Rate Limits
Beyond included monthly operations, bill overages via Stripe metered usage (typically $0.005–$0.02 per call with volume discounts).
| Tier | Operations/mo | Burst (req/s) |
|---|---|---|
| Free | 1,000 | 10 |
| Starter ($19) | 20,000 | 30 |
| Pro ($49) | 100,000 | 100 |
| Enterprise | Custom / unlimited | SLA |
Error Handling
All errors follow a consistent format:
{
"success": false,
"error": "INVALID_API_KEY",
"message": "API key is revoked or invalid"
}| Code | Status | Description |
|---|---|---|
| INVALID_API_KEY | 401 | API key missing or invalid |
| RATE_LIMITED | 429 | Too many requests |
| FACT_NOT_FOUND | 404 | Referenced fact does not exist |
| PROOF_FAILED | 400 | Unable to generate proof for claim |
| CLIENT_ENCRYPTED | 422 | Fact is client-sealed; prove/share need local decryption |
| VALIDATION_ERROR | 400 | Invalid request (schema, missing fields, malformed proof string, …) |
Ready to get started?
Generate your API key and start building privacy-preserving applications.
Get API Key