Everything you need to add cost tracking, caching, security, and observability to your AI agents. Zero code changes to your LLM logic.
TL;DR: Point your OpenAI/Groq/Anthropic SDK to https://plumb.samji.in/v1 instead of the provider URL. Add an x-tenant-id header. Done.
Go to /login and sign in with Google or GitHub. The Dev tier is free forever.
Go to Dashboard → API Keys → Create Key. Copy the sk-... key.
⚠️ The key is shown only once. Store it securely.
curl -X POST https://plumb.samji.in/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_LLM_PROVIDER_KEY" \
-H "x-proxy-key: sk-YOUR-PROXY-KEY" \
-H "x-upstream: groq" \
-d '{
"model": "llama-3.1-8b-instant",
"messages": [{"role": "user", "content": "Hello!"}]
}'Go to your dashboard — you'll see your request, token usage, cost, and cache status in real-time.
There are two separate keys involved:
Identifies you to our proxy. Sent as:
x-proxy-key: sk-your-proxy-keyCreated in your dashboard. Tracks your usage and applies your rate limits.
Your actual OpenAI/Groq/Anthropic key. Sent as:
Authorization: Bearer sk-your-openai-keyPassed through to the upstream provider. We never store it.
Important: The Authorization header is always passed through to the upstream LLM. We never read, log, or store your provider API key.
For quick testing, you can skip the proxy key and just use a tenant identifier:
# Simple mode — just identify yourself
curl -X POST https://plumb.samji.in/v1/chat/completions \
-H "Authorization: Bearer YOUR_LLM_KEY" \
-H "x-tenant-id: my-company" \
-H "x-upstream: groq" \
-d '{"model":"llama-3.1-8b-instant","messages":[{"role":"user","content":"Hi"}]}'Every request is broken down into 4 cost layers:
💡 Tip: Move large tool schemas to a shared system prompt to reduce per-request tool token costs.
Identical requests are served from cache instantly, saving LLM costs. Cache is per-tenant (no cross-tenant leaks).
x-cache: HIT header💡 Tip: Agents that retry the same question benefit massively. 30%+ cache hit rates are common on repetitive agent workloads.
Sensitive data is automatically masked before reaching the LLM provider:
[PII_SSN_0001][PII_EMAIL_0002][PII_CC_0003][PII_PHONE_0004]💡 Masking is reversible — authorized users can reveal original values via the vault API.
Prevent runaway agents from destroying your budget:
💡 Tip: Set conservative budget caps initially. You can always increase them from the dashboard.
Detects multi-step attack patterns that look benign individually:
x-session-id header💡 Tip: Always send x-session-id for multi-turn agent conversations to enable RiskChain.
If your primary provider goes down, requests auto-route to a backup:
Requests that exceed a model's context window are blocked before reaching the provider:
💡 Saves you from paying for requests that would fail anyway.
Detects when a session re-issues the same request intent repeatedly and stops it before it burns budget:
LOOP_MAX_REPEATS times (default 5) within LOOP_WINDOW_SECS (default 30s), the request is rejected429 with code agent_loop_detectedx-session-id (so separate conversations don't interfere)💡 Set LOOP_MAX_REPEATS=0 to disable. Tune LOOP_WINDOW_SECS higher for long-running agent sessions.
Scans both message content and tool definitions for adversarial patterns:
403 prompt_injection_blocked💡 This protects you from untrusted MCP servers and malicious RAG content injecting instructions into your agent's context.
A rule engine evaluates each request against configurable security policies:
block (reject 403), redact (mask matched content), alert (log + continue), log (silent record)GET /api/rules💡 Blocked requests return 403 policy_blocked with the rule name in the response body.
Every request is traced end-to-end with a unique ID and can be replayed deterministically:
x-trace-id headerGET /api/traces — list recent traces (admin-gated)GET /api/traces/{id} — inspect a single tracePOST /api/traces/{id}/replay — replay the original request and diff the response (hash comparison)💡 Use replay to verify that a policy change doesn't break working requests (regression testing for governance).
A tenant-isolated, multi-tier memory API for stateful agent workflows:
GET /api/memory/semantic/{key}/history)💡 Use working memory for scratchpad, episodic for observability, semantic for distilled knowledge your agents share across sessions.
Accurate spend tracking that accounts for provider prompt-cache discounts:
cached_tokens in usage responsescache_read_input_tokens and cache_creation_input_tokens💡 This means your cost dashboards show real costs, not inflated estimates — critical for accurate FinOps.
Set the x-upstream header to route to any provider:
| x-upstream | Provider | Free Tier? | Best For |
|---|---|---|---|
groq | Groq | ✅ Yes | Fastest inference, free Llama models |
openrouter | OpenRouter | ✅ Some models | Access to 100+ models via one key |
together | Together AI | ✅ $5 credit | Open-source models, fine-tuning |
| (default) | OpenAI | ❌ Paid | GPT-4o, best overall quality |
anthropic | Anthropic | ❌ Paid | Claude, 200k context window |
local | Ollama | ✅ Free | Local models, privacy |
💡 Start with Groq (free, fast) for testing. Switch to OpenAI/Anthropic for production quality.
from openai import OpenAI
client = OpenAI(
api_key="YOUR_LLM_KEY", # Your Groq/OpenAI key
base_url="https://plumb.samji.in/v1",
default_headers={
"x-proxy-key": "sk-your-proxy-key",
"x-upstream": "groq" # or "openai", "anthropic", etc.
}
)
response = client.chat.completions.create(
model="llama-3.1-8b-instant",
messages=[{"role": "user", "content": "Hello!"}]
)
print(response.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_LLM_KEY",
baseURL: "https://plumb.samji.in/v1",
defaultHeaders: {
"x-proxy-key": "sk-your-proxy-key",
"x-upstream": "groq",
},
});
const response = await client.chat.completions.create({
model: "llama-3.1-8b-instant",
messages: [{ role: "user", content: "Hello!" }],
});
console.log(response.choices[0].message.content);from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="llama-3.1-8b-instant",
openai_api_key="YOUR_GROQ_KEY",
openai_api_base="https://plumb.samji.in/v1",
default_headers={
"x-proxy-key": "sk-your-proxy-key",
"x-upstream": "groq"
}
)
response = llm.invoke("What is 2+2?")
print(response.content)curl -X POST https://plumb.samji.in/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_LLM_KEY" \
-H "x-proxy-key: sk-your-proxy-key" \
-H "x-upstream: groq" \
-d '{
"model": "llama-3.1-8b-instant",
"messages": [{"role": "user", "content": "Hello!"}]
}'| Method | Path | Description |
|---|---|---|
ANY | /v1/* | Proxy to LLM provider (OpenAI-compatible) |
ANY | /anthropic/* | Proxy to Anthropic API |
ANY | /mcp/* | Proxy to MCP server (JSON-RPC 2.0) |
| Method | Path | Description |
|---|---|---|
GET | /health | Health check (returns "ok") |
GET | /api/stats | Global metrics (requests, cache, cost, tokens) |
GET | /api/stats/history | Time-series metrics (10s intervals) |
GET | /api/tenants | List all tenants and their usage |
GET | /api/tenants/{slug}/stats | Per-tenant usage (daily spend, budget %) |
GET | /api/keys | List API keys |
POST | /api/keys | Create API key |
DELETE | /api/keys/{id} | Revoke an API key |
GET | /api/events | SSE stream of real-time events |
GET | /api/rules | List active security policy rules |
GET | /api/vault/tokens | List PII vault tokens (admin-gated) |
GET | /api/vault/reveal/{token} | Reveal original PII value (admin/security role, tenant-scoped) |
GET | /api/vault/audit | PII vault reveal audit log |
GET | /api/traces | List recent request traces (admin-gated) |
GET | /api/traces/{id} | Inspect a single trace with governance steps |
POST | /api/traces/{id}/replay | Replay a traced request and diff the response |
GET | /api/memory/working/{key} | Read working memory entry |
PUT | /api/memory/working/{key} | Write working memory entry |
GET | /api/memory/episodic | Query episodic event log |
POST | /api/memory/episodic | Append event to episodic memory |
GET | /api/memory/semantic/{key} | Read semantic fact (clearance-gated) |
PUT | /api/memory/semantic/{key} | Write/update semantic fact (CAS with expected_version) |
GET | /api/memory/semantic/{key}/history | Version history of a semantic fact |
GET | /api/memory/semantic | Search semantic memory by keyword |
POST | /auth/token | Get JWT token (testing only) |
| Header | Required | Description |
|---|---|---|
Authorization | ✅ | Your LLM provider key (passed through to upstream) |
x-proxy-key | Recommended | Your proxy API key (identifies tenant via DB) |
x-tenant-id | Alternative | Tenant slug (use if you don't have a proxy key) |
x-upstream | Optional | Override provider: groq, openrouter, anthropic, together, local |
x-session-id | Optional | Session ID for RiskChain multi-step tracking and loop detection |
x-agent-id | Optional | Agent identity for per-agent stats and anomaly detection |
| Header | Description |
|---|---|
x-cache | HIT if served from cache (no LLM call made) |
x-trace-id | Unique trace identifier for this request (always present) |
x-agent-safefix | SafeFix suggestion — a safer alternative command when the request contains risky operations (present only when triggered) |
Minimal. The proxy adds ~30µs p50 / ~55µs p99 of governance overhead (measured via a deterministic micro-benchmark gating CI). Cache hits are served instantly (0ms to upstream). The latency you see is almost entirely from the LLM provider.
No. The Authorization header is passed through to the upstream provider without being logged or stored. We only read the x-proxy-key and x-tenant-id headers.
You'll get a 429 Too Many Requests response. Budget caps reset daily at midnight UTC. You can increase your cap from the dashboard or by upgrading your tier.
Cache is keyed on the semantic intent of your request (model + messages + tools). Cache TTL is 15 minutes. Different users/tenants never share cache. If you need fresh responses, slightly vary your prompt.
Yes. SSE streaming (stream: true) is fully supported. Token tracking works for both streaming and non-streaming responses.
The circuit breaker detects 3 consecutive failures and automatically routes to a backup provider for 30 seconds. You'll see a "failover" event in your activity feed. Note: the backup provider must support the same model, or you'll need to handle model differences in your code.
We store: request metadata (model, token counts, cost, path, timestamp). We do NOT store: request/response bodies, your prompts, your LLM keys, or model outputs. PII is masked in-flight and never persisted.
Three ways: (1) Dashboard at /dashboard, (2) API at plumb.samji.in/api/tenants/YOUR-SLUG/stats, (3) SSE event stream at plumb.samji.in/api/events for real-time monitoring.
Use deterministic system prompts. Avoid timestamps or random IDs in messages. The more consistent your requests, the higher your cache hit rate.
Only include tools the agent actually needs for each request. Sending 20 tool definitions when only 2 are relevant wastes tokens on every call.
Always send x-session-id for multi-turn conversations. This enables RiskChain to detect attack patterns across turns.
Groq offers free Llama 3.1 inference with the fastest speeds. Perfect for development and testing before switching to paid providers.
A single agent loop can burn through hundreds of dollars in minutes. Set a conservative daily cap ($1-5 for dev, $50+ for production).
Keep curl -N plumb.samji.in/api/events running in a terminal during development to see every event in real-time.