๐Ÿ›ก๏ธ Interven
Reference

SDK Reference

Method-by-method reference for the Python and JS/TS SDKs.

This is the formal reference. For usage patterns and getting-started examples, see Python SDK and JavaScript / TypeScript.

Python: interven

Client

The base client. Manages auth + HTTP transport.

from interven import Client

client = Client(
    api_key="iv_live_...",        # default: env INTERVEN_API_KEY
    gateway_url="https://...",    # default: env INTERVEN_GATEWAY_URL or hosted SaaS
    timeout=30.0,                 # seconds; default 30
    retries=2,                    # 5xx retries; default 2
    agent_id="...",               # default: env INTERVEN_AGENT_ID
    agent_name="...",             # default: env INTERVEN_AGENT_NAME
)

Client.scan(...)

verdict = client.scan(
    method: str,                            # required โ€” "POST", "GET", etc.
    url: str,                               # required โ€” absolute upstream URL
    body: Any = None,                       # request body (dict, list, str)
    headers: dict | None = None,            # request headers
    agent_id: str | None = None,            # override default agent
    agent_name: str | None = None,
    runtime_type: str | None = None,        # "langchain", "langgraph", etc.
    context: str | None = None,             # conversation context for semantic engine
    env: str | None = None,                 # override environment binding
) -> ScanVerdict

Returns a ScanVerdict:

FieldTypeNotes
decisionLiteral["ALLOW", "DENY", "SANITIZE", "REQUIRE_APPROVAL"]
risk_scorefloat0.0โ€“1.0
risk_bandLiteral["LOW", "MED", "HIGH", "CRITICAL"]
reason_codeslist[str]e.g. ["SECRET_DETECTED", "EXTERNAL_PRINCIPAL"]
trace_idstrUUID
tool_namestre.g. "slack", "custom_proxy"
operationstre.g. "post_message"
classificationslist[str]["SECRETS", "PII", "PHI", "INTERNAL"]
signalslist[dict]Per-engine output
sanitized_bodyAny | NonePresent when decision is SANITIZE
approval_idstr | NonePresent when decision is REQUIRE_APPROVAL
policy_idstr | NoneFirst matched policy (others in signals)
policy_namestr | None

Client.scan_response(...)

client.scan_response(
    trace_id: str,                  # from the original scan
    response_body: Any,             # upstream response (object or text)
    response_status: int = 200,
) -> ScanResponseResult

Returns ScanResponseResult with data_classes, fields_redacted_count, and content_fingerprint. Used for read-then-write exfil correlation.

Client.wait_for_approval(...)

final = client.wait_for_approval(
    approval_id: str,
    timeout: float = 60.0,          # seconds
    poll_interval: float = 2.0,     # seconds between polls
) -> ApprovalResult

Blocks until the analyst decides or timeout elapses. Returns ApprovalResult(status="approved" | "denied" | "pending" | "expired", reason=...).

Client.mint_ephemeral_key(...)

result = client.mint_ephemeral_key(
    ttl_seconds: int = 600,
    max_uses: int = 1,
    scope: dict | None = None,      # {"tools": [...], "verbs": [...]}
    description: str | None = None,
) -> EphemeralKey

Returns EphemeralKey(key=..., ephemeral_id=..., expires_at=..., scope=...). The key field is the full key โ€” shown once. See Ephemeral Keys.

Client.health()

ok = client.health()  # -> bool

Returns True if the gateway is reachable + auth works.

guard decorator

from interven import guard

@guard(
    api_key: str | None = None,           # default: env
    tool: str | None = None,              # default: auto-detect from function name + body
    operation: str | None = None,         # default: auto-detect
    on_block: Literal["raise", "return_none"] = "raise",
)
def your_function(...):
    ...

Scans the function's arguments against /v1/scan. On DENY, raises InterventBlockedError (or returns None if on_block="return_none"). On SANITIZE, the function is called with the redacted args.

gate one-liner

from interven import gate

result = gate(
    method: str,
    url: str,
    body: Any = None,
    headers: dict | None = None,
    api_key: str | None = None,
) -> ScanVerdict

Same return shape as Client.scan(). Useful for fire-and-handle code paths that don't want to hold a Client instance.

Exceptions

ExceptionWhen
InterventBlockedErrorguard-decorated function got a non-ALLOW decision
AuthenticationErrorAPI key invalid / missing / revoked
RateLimitErrorHTTP 429 returned
QuotaExceededErrorHTTP 429 with QUOTA_EXCEEDED reason
GatewayErrorHTTP 5xx after retries
TimeoutErrorPer-request timeout reached

All inherit from InterventError.


JavaScript / TypeScript: @interven/sdk-js

IntervenClient

import { IntervenClient } from '@interven/sdk-js';

const client = new IntervenClient({
  apiKey: 'iv_live_...',                  // default: env INTERVEN_API_KEY
  gatewayUrl: 'https://...',              // default: env INTERVEN_GATEWAY_URL or hosted
  timeoutMs: 30_000,                      // default 30s
  retries: 2,
  agentId: '...',
  agentName: '...',
});

client.scan(...)

const verdict = await client.scan({
  method: string,
  url: string,
  body?: unknown,
  headers?: Record<string, string>,
  agentId?: string,
  agentName?: string,
  runtimeType?: string,
  context?: string,
  env?: string,
});

Returns ScanResponse:

{
  decision: 'ALLOW' | 'DENY' | 'SANITIZE' | 'REQUIRE_APPROVAL';
  risk_score: number;
  risk_band: 'LOW' | 'MED' | 'HIGH' | 'CRITICAL';
  reason_codes: string[];
  trace_id: string;
  tool_name: string;
  operation: string;
  classifications: string[];
  signals: Array<{ engine: string; score: number; details: any }>;
  sanitized_body?: unknown;
  approval_id?: string;
  policy_id?: string;
  policy_name?: string;
}

client.scanResponse(...)

await client.scanResponse({
  traceId: string,
  responseBody: unknown,
  responseStatus?: number,
});

client.waitForApproval(...)

const final = await client.waitForApproval(approvalId, {
  timeoutMs: 60_000,
  pollIntervalMs: 2_000,
});
// -> { status: 'approved' | 'denied' | 'pending' | 'expired', ... }

client.mintEphemeralKey(...)

const ek = await client.mintEphemeralKey({
  ttlSeconds: 600,
  maxUses: 1,
  scope: { tools: ['slack'], verbs: ['WRITE'] },
  description: 'One-shot Slack post',
});
// -> { key: 'iv_eph_...', ephemeralId, expiresAt, scope }

client.health()

const ok = await client.health();  // boolean

guard decorator

import { guard } from '@interven/sdk-js';

const safe = guard(yourAsyncFn, {
  apiKey: 'iv_live_...',           // default: env
  tool: 'slack',                   // optional
  operation: 'post_message',       // optional
  onBlock: 'throw',                // 'throw' (default) or 'returnNull'
});

// throws GuardBlockedError on non-ALLOW (unless onBlock = 'returnNull')
await safe(args);

gate one-liner

import { gate } from '@interven/sdk-js';

const result = await gate('POST', 'https://...', {
  body: { ... },
  apiKey: 'iv_live_...',
});

if (result.decision === 'ALLOW') {
  // forward result.body to upstream
}

Error classes

ClassWhen
GuardBlockedErrorguard returned non-ALLOW
AuthenticationError401
RateLimitError429 (rate)
QuotaExceededError429 (quota)
GatewayError5xx after retries

All inherit from IntervenError.

TypeScript types

import type {
  Decision,
  RiskBand,
  ScanResponse,
  GateResult,
  IntervenClientOptions,
  GuardOptions,
  EphemeralKeyOptions,
  ApprovalStatus,
} from '@interven/sdk-js';

See also