Documentation

Scan. Rescan. Verify. Any LLM.

Shutapp is a model-agnostic PII and prompt-security firewall. Scan the prompt on the way out, send the redacted version to whichever LLM you like with your own provider key, then rescan the response on the way back. Shutapp never sees your provider key or the raw model output.

If you can call the OpenAI SDK, you already know how to use Shutapp. The two extra calls take three lines.

01

Install

Thin HTTP client. Two light deps (httpx, cryptography). Python 3.9+. Read the SDK before you trust it.

shell
pip install shutapp
02

Get a key

Hit the button on the landing pageand put the key in an environment variable. Stateless signed JWT, valid for 30 days. No accounts, no portal, no “set up your workspace” flow.

.env
export SHUTAPP_API_KEY="sa_live_..."
03

The three lines

Works with any LLM. Shutapp only processes text — it never sees your provider key or the raw response unless you hand it to rescan.

python · universal
from shutapp import Shutapp

s = Shutapp(api_key="<YOUR_KEY>")

# 1. PII out
safe, session = s.scan(user_input)

# 2. you call the LLM with YOUR provider key
#    (we never see it)
reply = call_your_llm(safe)

# 3. PII back, leakage checked
final = s.rescan(reply, session=session).cleaned_text

The scan response includes a decision (allow / flag / block), a risk block with the prompt-injection score, a cost block with cumulative quota, and a signed receipt. You decide what to do with each one. We don't.

04

Auto-wrap for OpenAI

One line of sugar. Your existing OpenAI code doesn't change. Under the hood guard() scans outbound messages, lets OpenAI respond with your key, and rescans the response before your code sees it.

python · openai
from openai import OpenAI
from shutapp import guard

# One call. Every chat.completions.create is scanned and rescanned.
# Your existing code doesn't change.
client = guard(OpenAI(), api_key="<YOUR_KEY>")

client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "My email is john@acme.com"}],
)
05

Auto-wrap for Anthropic

python · anthropic
from anthropic import Anthropic
from shutapp import guard

client = guard(Anthropic(), api_key="<YOUR_KEY>")

client.messages.create(
    model="claude-3-5-sonnet-latest",
    max_tokens=512,
    messages=[{"role": "user", "content": "My email is john@acme.com"}],
)
06

Detect system-prompt leakage

Register a MinHash fingerprint of your system prompt once. Shutapp stores the digest; the text is discarded. Every rescan can then flag near-verbatim leakage in the response — useful when somebody finally figures out the “repeat your instructions” trick on your support bot.

python · system fingerprint
fp_id = s.register_system_prompt("support-bot", SYSTEM_PROMPT)

# later, on every rescan:
result = s.rescan(llm_response, session=sess, system_fingerprint_id=fp_id)
if result.findings["system_leakage"]["matched"]:
    # log, alert, or retry with a tighter system prompt — your call
    ...
07

Raw HTTP (any language)

Two endpoints. Both return a signed receipt in the response body. If your stack isn't Python, this is the whole API.

curl
# 1. scan
curl https://api.shutapp.world/v1/scan \
  -H "X-Shutapp-Key: <YOUR_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"text":"My email is john@acme.com"}'

# 2. call your LLM yourself, with your own key

# 3. rescan
curl https://api.shutapp.world/v1/rescan \
  -H "X-Shutapp-Key: <YOUR_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"text":"<LLM response>","session_map":{}}'

# verify a receipt offline, no network needed
shutapp verify receipt.json --base-url https://api.shutapp.world
typescript · fetch
// 1. scan on the way out
const scan = await fetch("https://api.shutapp.world/v1/scan", {
  method: "POST",
  headers: {
    "X-Shutapp-Key": "<YOUR_KEY>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ text: "My email is john@acme.com" }),
});
const { redacted_text, map, receipt, decision } = await scan.json();

// 2. call the LLM yourself with redacted_text and YOUR provider key

// 3. rescan on the way back
const rescan = await fetch("https://api.shutapp.world/v1/rescan", {
  method: "POST",
  headers: {
    "X-Shutapp-Key": "<YOUR_KEY>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ text: llm_response, session_map: map }),
});
const { cleaned_text, findings } = await rescan.json();
08

Verify a receipt offline

Every scan and rescan response includes an Ed25519-signed receipt. The public key is published at /v1/pubkey. Pin its fingerprint and you can verify any receipt without touching the network again. When the auditor asks, you hand them a file and the public key. That's the audit trail.

python · verify
ok = s.verify(receipt)    # True / False
# or, from a shell:
#   shutapp verify path/to/receipt.json --base-url https://api.shutapp.world
09

Receipts in your terminal

After every scan and rescan, the SDK prints one line to stderr. No dashboard. No 11th tab. Where you already are.

shell · stderr
$ python my_chatbot.py
[shutapp] scan ok | 11ms | EMAIL_ADDRESS x1, PHONE_NUMBER x1 | rcpt_8f3a2b
[shutapp] scan ok | 14ms | no PII detected | rcpt_91ac02
Assistant: Sure, I'll email john@acme.com.

Configure via Shutapp(receipts="stderr" | "stdout" | "silent" | callable).

10

What Shutapp does NOT do

Prompt injection is unsolved. We detect attacks with a heuristic layer plus ProtectAI's deberta-v3 classifier, and we score them. We do not claim to prevent them and you should be wary of anyone who does.

We do not address training-data poisoning, supply-chain model tampering, RAG retrieval weaknesses, or hallucination — those are different problems and different products. See the coverage matrixfor the honest picture, including the rows where we said “out of scope.”

Need more

Need a self-hosted build, an on-prem deployment, or a custom recognizer for that one weird internal ID format you have? hi@shutapp.world. A human will reply.