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
| Field | Meaning |
|---|
request_id | Echo of the X-Request-ID response header. Always quote this in support tickets. |
policy_id / policy_slug | Which combined policy ran. |
overall_status | OK (every analyzer passed), TERMINATED_EARLY (a termination rule fired), or ERROR (one or more analyzers errored). |
terminated_early | Boolean shortcut for overall_status == "TERMINATED_EARLY". |
termination_reason | Present when terminated_early == true. Names the analyzer and the rule that fired. |
analyzer_results | Per-analyzer block. Status is OK, TERMINATED_EARLY, ERROR, or SKIPPED. |
aggregated_metrics | Sum 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:
| Field | Meaning |
|---|
status | OK, TERMINATED_EARLY, ERROR, SKIPPED. |
output | Analyzer-specific structured output. See the per-analyzer page. |
metrics | Map of metric name → value. The dashboard editor exposes the same names. |
terminated_by | Present when this analyzer is the one that terminated the run. |
error | Present 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:
| Field | Meaning |
|---|
analyzer | The server key of the analyzer whose rule fired. |
rule | A human-readable description of which signal terminated. |
match | Present when output_match was used. The matched substring. |
metric / value / operator | Present 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:
| Metric | Meaning |
|---|
total_processing_time_ms | Sum of analyzer wall-clock. Useful for SLO tracking. |
total_cost_usd | Sum of analyzer cost (when each analyzer reports a cost). |
The numbers are summed only over analyzers that ran; skipped
analyzers contribute zero.
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.