Docs · Inicio rápido
Suelta el hook, o llama al SDK. Una sola ida y vuelta.
Dos formas de conectar tu agente a la API que decide. Ambas responden allow / review / deny, con una razón en lenguaje claro y el desglose de cada señal.
1 · Hook de Claude CodeSin código — un hook PreToolUse corre antes de que el agente haga nada, así que se puede bloquear. Apúntalo a la API.
.claude/settings.json
// .claude/settings.json — one-line Claude Code hook
{
"hooks": {
"PreToolUse": [{
"matcher": "*",
"hooks": [{
"type": "http",
"url": "https://specter-decision-api.fly.dev/hooks/claude-code",
"headers": { "x-api-key": "$SPECTER_API_KEY" },
"allowedEnvVars": ["SPECTER_API_KEY"],
"timeout": 5
}]
}]
}
}El truco (no lo “arregles”): un hook HTTP no bloquea respondiendo con un código de error. Un 4xx/5xx solo registra un error y la herramienta corre igual. El hook siempre responde HTTP 200 — el permitir/bloquear va en el cuerpo, no en el código de estado. Para bloquear, el cuerpo trae permissionDecision: "deny". Specter hace exactamente eso. (Los hooks de tipo comando bloquean con código de salida 2; el código 1 solo advierte.)
2 · SDKVerifica cualquier acción desde tu propio código.
guard.ts
// npm i specter-sdk
import { Guard } from 'specter-sdk';
const guard = new Guard({ apiUrl: process.env.SPECTER_API_URL!, apiKey: process.env.SPECTER_API_KEY! });
const decision = await guard.check({
agentId: 'shop-agent',
sessionId: 'sess_42',
action: { type: 'payment', amount: 79.99, currency: 'USD',
destination: 'acct_acme_store', merchantClaimed: 'Acme Store', rawInput: {} },
context: { userPrompt: 'Buy the Acme mouse from Acme Store under $100.',
destinationOrigin: 'user_prompt', establishedMerchant: 'Acme Store' },
});
if (decision.decision !== 'allow') throw new Error(decision.reason);
// ...only now move money.Petición — POST /v1/evaluate
POST /v1/evaluate
Authorization: Bearer <SPECTER_API_KEY>
Content-Type: application/json
{
"agentId": "shop-agent",
"sessionId": "sess_42",
"action": {
"type": "payment", // payment | refund | db_write | shell | file | other
"amount": 79.99,
"currency": "USD",
"destination": "acct_acme_store",
"merchantClaimed": "Acme Store",
"rawInput": {}
},
"context": {
"userPrompt": "Buy the Acme mouse from Acme Store under $100.",
"destinationOrigin": "user_prompt", // user_prompt | ingested_content | tool_output | unknown
"sourceRefs": ["firecrawl:https://shop.example/acme"],
"establishedMerchant": "Acme Store"
}
}Respuesta
200 OK
{
"decision": "deny", // allow | deny | review
"riskScore": 0.78, // 0..1
"reason": "Blocked: payee originated from ingested_content and does not trace back to the user's request.",
"signals": {
"provenance": "payee originated from ingested_content ...",
"policy": "never-before-seen account — requires human approval",
"consistency": "destination does not correspond to claimed merchant",
"llm": "llm: payee does not match the user's merchant; likely injected"
},
"signalDetail": [
{ "id": "provenance", "score": 1, "verdict": "..." },
{ "id": "policy", "score": 0.6, "verdict": "..." }
]
}GET/v1/audit
Tu registro a prueba de manipulación, página por página.
GET/v1/audit/verify
Vuelve a revisar el registro para probar que no se alteró → { valid, brokenAt }.
POST/hooks/claude-code
Convierte un payload de PreToolUse en una verificación. Siempre responde 200.
Auth, límites, integraciones
- • Auth: pasa Authorization: Bearer <key> o x-api-key. Cada clave está ligada a tu cuenta.
- • Límites de tasa: por clave (por defecto 60 de ráfaga / 30 rps); recibes un 429 si te pasas.
- • Integraciones: el hook de Claude Code, specter-sdk, o un proxy delante de tus herramientas.
- • Velocidad: mantenido en caliente en Fly, menos de 500ms en el 99% de las llamadas.
