Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.lasscyber.com/llms.txt

Use this file to discover all available pages before exploring further.

This page explains what analyze() returns. The shape is the same whether you call the HTTP endpoint, the Python SDK, or the TypeScript SDK; the SDKs add a small ergonomic layer on top.

High-level shape

{
  "request_id": "5b3f6c7e-7d24-4d40-9b12-3a59c01c6e91",
  "policy_id": "cdc7…",
  "policy_slug": "default-inbound",
  "overall_status": "TERMINATED_EARLY",
  "terminated_early": true,
  "termination_reason": {
    "analyzer": "adversarial_detection_analyzer",
    "rule": "score >= 0.85 AND output_match INJECTION/JAILBREAK"
  },
  "analyzer_results": {
    "adversarial_detection_analyzer": { ... },
    "safety_moderation_analyzer":     { "status": "SKIPPED" },
    "dlp_analyzer":                   { "status": "SKIPPED" },
    "url_analyzer":                   { "status": "SKIPPED" },
    "yara_analyzer":                  { "status": "SKIPPED" }
  },
  "aggregated_metrics": {
    "total_processing_time_ms": 38.4,
    "total_cost_usd": 0.0002
  }
}
The exact field names follow the OpenAPI schema in sdk/openapi/openapi.json; the auto-rendered API reference is the authoritative source.

Top-level fields

FieldMeaning
request_idEcho of the X-Request-ID response header. Always quote this in support tickets.
policy_id / policy_slugWhich combined policy ran.
overall_statusOK (every analyzer passed), TERMINATED_EARLY (a termination rule fired), or ERROR (one or more analyzers errored).
terminated_earlyBoolean shortcut for overall_status == "TERMINATED_EARLY".
termination_reasonPresent when terminated_early == true. Names the analyzer and the rule that fired.
analyzer_resultsPer-analyzer block. Status is OK, TERMINATED_EARLY, ERROR, or SKIPPED.
aggregated_metricsSum of per-analyzer wall-clock and cost. Only present when the policy’s default_telemetry is true.

SDK ergonomics

The SDKs unpack the body into a Decision object:
decision = agnes.analyze("...", policy="default-inbound")

decision.allowed         # bool — False if terminated_early else True
decision.blocked_by      # tuple[str, ...] — canonical names of analyzers that blocked
decision.reasons         # tuple[str, ...] — human-readable reasons
decision.request_id      # str
decision.raw             # dict — the full response body
blocked_by / blockedBy is the easiest lever for branching in application code:
if not decision.allowed:
    if "prompt-injection-jailbreak" in decision.blocked_by:
        return safe_refusal()
    if "sensitive-data" in decision.blocked_by:
        return scrub_and_retry(decision.raw)
    return generic_block_message()
The SDK translates server keys (adversarial_detection_analyzer) to canonical names (prompt-injection-jailbreak) so your code stays stable even if the server-side key flips later. See Versioning.

Per-analyzer blocks

Every analyzer reports its result in analyzer_results.<server_key>. The shape is consistent across analyzers:
FieldMeaning
statusOK, TERMINATED_EARLY, ERROR, SKIPPED.
outputAnalyzer-specific structured output. See the per-analyzer page.
metricsMap of metric name → value. The dashboard editor exposes the same names.
terminated_byPresent when this analyzer is the one that terminated the run.
errorPresent when status == "ERROR".
Skipped analyzers carry only status: "SKIPPED" — they were declared in the policy but never reached because an earlier analyzer terminated.

Example: an inbound block by the prompt-injection classifier

"analyzer_results": {
  "adversarial_detection_analyzer": {
    "status": "TERMINATED_EARLY",
    "output": {
      "label": "INJECTION/JAILBREAK",
      "score": 0.97
    },
    "metrics": {
      "score": 0.97,
      "inference_time_ms": 38.4
    },
    "terminated_by": {
      "rule": "score >= 0.85 AND output_match INJECTION/JAILBREAK",
      "metric": "score",
      "value": 0.97,
      "operator": ">="
    }
  },
  "safety_moderation_analyzer": { "status": "SKIPPED" },
  "dlp_analyzer":                { "status": "SKIPPED" },
  "url_analyzer":                { "status": "SKIPPED" },
  "yara_analyzer":               { "status": "SKIPPED" }
}
The classifier scored 0.97 on INJECTION/JAILBREAK, which crossed both the score threshold (>= 0.85) and the output match in default-inbound. The execution engine terminated immediately and flipped overall_status to TERMINATED_EARLY. The remaining analyzers were skipped.

Example: outbound flag by safety + SDP

"analyzer_results": {
  "safety_moderation_analyzer": {
    "status": "TERMINATED_EARLY",
    "output": {
      "is_safe": false,
      "categories": [
        { "name": "Hate Speech", "score": 0.88, "verdict": "violation" }
      ]
    },
    "metrics": {
      "max_violation_score": 0.88,
      "violation_category_count": 1,
      "inference_time_ms": 142.0
    }
  }
}
The safety judge fired on hate speech with confidence 0.88. The combined policy’s is_safe boolean rule terminated the run.

Errors at the analyzer level

A single analyzer error does not always mean the whole call errored. Behavior depends on the step type:
  • In a sequential step, the first analyzer that errors stops the step and flips overall_status to ERROR.
  • In an asynchronous step, every analyzer in the group still runs to completion. Any error in the group flips overall_status to ERROR, but you’ll see the other analyzers’ results too.
When overall_status == "ERROR" and the cause is upstream infrastructure (model service unreachable, DLP API down), the API response is HTTP 503 with code: "analyzer_unavailable" instead of a 200 with embedded errors. SDKs retry these automatically. See analyzer_unavailable.

Termination reason

When terminated_early == true, the response carries a structured termination_reason:
FieldMeaning
analyzerThe server key of the analyzer whose rule fired.
ruleA human-readable description of which signal terminated.
matchPresent when output_match was used. The matched substring.
metric / value / operatorPresent when a threshold rule fired.
This is the easiest way to surface a precise message to your end user without parsing every analyzer block.

Aggregated metrics

When the policy’s default_telemetry is true, the response carries totals across the run:
MetricMeaning
total_processing_time_msSum of analyzer wall-clock. Useful for SLO tracking.
total_cost_usdSum of analyzer cost (when each analyzer reports a cost).
The numbers are summed only over analyzers that ran; skipped analyzers contribute zero.

Headers worth inspecting

  • X-Request-ID — same value as request_id in the body.
  • X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset — current rate-limit window state.
  • X-Billing-Status, X-Subscription-Status — subscription health. active is normal; past_due / canceled flag attention.
  • X-Agnes-Test-Mode: true — sandbox response. Exclude from billing dashboards.

Common questions

Why does blocked_by have multiple analyzers? Asynchronous steps can have more than one analyzer terminate in the same step. The SDK reports every analyzer that fired a terminate_immediately rule. Where is the prompt in the response? It is not echoed back. The SDKs keep the input you sent locally; the server does not persist it unless you explicitly ingest into the threat-intel store. Can I get the cost in tokens, not dollars? total_cost_usd is the public field. The metered usage report at agnes.lasscyber.com/agnes-info/billing shows token-level metering for billing reconciliation.

Next

  • Errors — what every error response looks like.
  • Combined analyzer — author the termination rules that decide what terminated_early means.
  • API reference — full auto-rendered schema with interactive playground.