POST /api/v1/analyze
Analyzes a text for signals of irregular conduct. The endpoint is stateless: each request is independent and does not store the submitted text. The result is a risk assessment that the caller can use to make triage decisions or escalate to legal review.
Authentication
Requires an active API key with the analyze:run scope.
Authorization: Bearer vario_YOUR_API_KEY
The API key is generated from the developer portal. To generate or rotate a key, see Authentication & API keys.
Rate limit
See Errors & rate limits for per-tier limits and the behavior of the Retry-After header.
Request body
Content-Type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Yes | Text to analyze. Maximum 8,000 characters (approximately 1,300 words). For longer documents, split the content into chunks and send separate requests. |
language | string | Yes | Language of the text. Values: "es" (Spanish) or "pt" (Portuguese). |
playbook | string | Yes | Type of conduct to detect. Values: "price_fixing" · "bid_rigging" · "bribery" · "market_manipulation" · "information_exchange" · "group_boycott". Query GET /api/v1/playbooks for the current list and the languages supported by each type. |
mode | string | No | Analysis mode. "screening": L1 layer only (keyword + embedding, no LLM) — 1 credit, the most economical, for mass corpus sweep. "fast": L1 + L2 (light LLM) — 2 credits, moderate precision. "full" (default): L1 + L2 + L3 (deep LLM) — 3 credits, maximum precision with full reasoning. |
thread_context | array[string] | No | Up to 10 previous messages in the thread, in chronological order. Each element has a maximum of 500 characters. Providing thread context improves analysis precision. Default: []. |
external_id | string | No | Your system's identifier for this message (for example, the email ID in your database). vario does not process this value — it returns it as-is in the response to facilitate traceability in your integration. |
Response
Content-Type: application/json · HTTP 200 OK
| Field | Type | Description |
|---|---|---|
combined_score | float [0–10] | Risk score from 0 to 10. Higher values indicate a stronger risk signal. |
should_alert | boolean | true if the analysis detected a risk signal sufficient to require review. |
severity | string | Risk category: "critical" · "high" · "medium" · "low". |
reasoning | string | Explanation of the result in the language of the analysis. May be empty if deep analysis was not available for this call. |
key_phrases | array[string] | Exact fragments of the text that the system identified as evidence of the evaluated conduct. |
regulatory_frameworks | array[string] | Regulatory frameworks applicable to the analyzed conduct type. |
conduct_type | string | Conduct detected by the pipeline. Open classification: may differ from the requested playbook. Closed domain of 7 values: "price_fixing" · "bid_rigging" · "bribery" · "market_manipulation" · "information_exchange" · "group_boycott" · "none". Always "none" when processing_path="l1_discard". Absent in screening mode. |
recommended_action | string | Suggested action. Closed domain, tier-dependent — fast/l2_pass/degraded: "no_action" · "monitoring" · "legal_review"; full mode (L3 passthrough): adds "open_case" · "dismiss" · "immediate_escalation". This is a system recommendation; the reviewer decides. |
confidence | string | Confidence level of the result: "high" · "medium" · "low". |
processing_path | string | null | Pipeline stage that produced this response. Non-breaking field: clients that do not know it ignore it without error. See table below. |
mode_used | string | Confirms the mode executed in this call: "screening", "fast", or "full". In screening mode the response schema is different — see the section below. |
analyzed_at | string | ISO 8601 UTC timestamp of the analysis moment. |
pipeline_version | string | Version of the detection engine (opaque string). Useful for traceability if behavior changes between versions. |
bedrock_available | boolean | Indicates whether deep analysis was available for this call. If false, the response is partial. |
processing_path — pipeline behavior
The pipeline applies early-exit gates to avoid unnecessary invocations. The processing_path field identifies which stage produced the response.
| Value | When | Bedrock invoked | Latency |
|---|---|---|---|
"l1_discard" | L1 score below minimum threshold. combined_score=0.0. Text was not analyzed by any LLM. | No | < 200 ms |
"screening" | screening mode — L1 scores only. | No | 20–150 ms |
"l2_pass" | L2 reliable (low or high score), no urgent signal. L3 not necessary. | L2 | < 1.5 s |
null | fast mode completed (L2, no L3 gate by design). Also returned when Bedrock failed in L2 (degraded path) — in that case bedrock_available=false. Disambiguate with bedrock_available. | L2 | 200–800 ms |
| (additional values) | full mode may return additional values identifying the depth of deep analysis. Pending documentation — use .unknown() or allow-unknown-enum-values for forward compatibility. | L2 + L3 | 2–8 s |
Response in screening mode
When mode is "screening", the response has a different schema from fast and full modes. It only includes L1 layer results (keyword + embedding). The L2/L3 fields (combined_score, should_alert, severity, reasoning, etc.) are not present.
| Field | Type | Description |
|---|---|---|
mode_used | string | "screening" |
l1_score | float [0–1] | Aggregated L1 score: max(l1_keyword_score, l1_embedding_score). |
l1_keyword_score | float [0–1] | Keyword detection score. |
l1_embedding_score | float [0–1] | Semantic similarity score via embeddings. |
l1_matched_terms | array[string] | Terms in the text that triggered L1 detection. |
l1_embedding_available | boolean | true if the embedding service was available for this call. |
playbook | string | Echo of the playbook in the request. |
playbook_version | string | Version of the playbook used. |
pipeline_version | string | Version of the analysis engine. |
analyzed_at | string (ISO 8601) | UTC timestamp of the analysis. |
external_id | string | null | Echo of the external_id sent in the request. |
Example response in screening mode:
{
"mode_used": "screening",
"l1_score": 0.82,
"l1_keyword_score": 0.75,
"l1_embedding_score": 0.82,
"l1_matched_terms": ["acordamos no movernos del precio", "precio"],
"l1_embedding_available": true,
"playbook": "price_fixing",
"playbook_version": "1.0",
"pipeline_version": "1.0",
"analyzed_at": "2026-06-04T10:15:00Z",
"external_id": "email-msg-00842"
}
Degradation behavior
If the deep analysis service is not available, the endpoint responds with HTTP 200 and bedrock_available: false. In that case, reasoning may be empty and combined_score reflects only the local analysis.
If you prefer to receive an explicit error instead of a partial response, add the header:
Prefer: deep-analysis-required
With this header, the endpoint responds 503 DEEP_ANALYSIS_UNAVAILABLE when deep analysis is not available.
Complete example
Request
curl -X POST https://api.vario.lat/v1/analyze \
-H "Authorization: Bearer vario_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Acordamos no bajar de $450 la unidad. Los demás van a hacer lo mismo esta semana.",
"language": "es",
"playbook": "price_fixing",
"mode": "full",
"thread_context": [
"La reunión de ayer salió bien, creo que todos están alineados.",
"Confirmo que el acuerdo sigue en pie para el trimestre."
],
"external_id": "email-msg-00842"
}'
Response
{
"combined_score": 7.7,
"should_alert": true,
"severity": "critical",
"reasoning": "El mensaje contiene una referencia directa a un precio acordado entre partes que actúan como competidores, con indicios de coordinación horizontal. El contexto del hilo refuerza la señal al mostrar continuidad de un acuerdo previo.",
"key_phrases": [
"no bajar de $450 la unidad",
"los demás van a hacer lo mismo"
],
"regulatory_frameworks": [
"Lei 12.529/2011 Art. 36",
"DL 211 Art. 3",
"COFECE Lineamientos de colusión"
],
"recommended_action": "legal_review",
"confidence": "high",
"mode_used": "full",
"analyzed_at": "2026-06-02T18:45:00Z",
"pipeline_version": "1.0",
"bedrock_available": true,
"external_id": "email-msg-00842"
}
Errors specific to this endpoint
For the complete reference of error codes and rate limits, see Errors & rate limits.
| HTTP | Code | Cause |
|---|---|---|
400 | INVALID_PLAYBOOK | The playbook value does not exist in the catalog. |
400 | INVALID_LANGUAGE | The specified language is not supported for that playbook. |
400 | INVALID_MODE | The mode value is not valid. Accepted values: "screening", "fast", "full". |
400 | TEXT_TOO_LONG | The text field exceeds 8,000 characters. |
400 | CONTEXT_TOO_LONG | thread_context exceeds 10 elements, or one of them exceeds 500 characters. |
401 | INVALID_API_KEY | The API key is invalid, inactive, or expired. |
401 | MISSING_AUTH | The Authorization header is not present. |
402 | INSUFFICIENT_CREDITS | The tenant's credit balance is 0. |
403 | INSUFFICIENT_SCOPE | The API key does not have the analyze:run scope. |
422 | VALIDATION_ERROR | The request body does not conform to the schema. |
429 | RATE_LIMIT_EXCEEDED | The call limit was exceeded. |
503 | DEEP_ANALYSIS_UNAVAILABLE | Only returned if Prefer: deep-analysis-required was sent and deep analysis is not available. |
500 | PIPELINE_ERROR | Internal error in the detection system. |
Usage notes
- Chunking: For texts exceeding 8,000 characters, split the content into fragments and send one request per fragment. Use
thread_contextto pass thread context, not to fragment thetextfield. - Three-stage funnel: When volume is high, use
mode: "screening"to discard text without L1 signal (1 credit),mode: "fast"for those that pass the threshold (2 credits), andmode: "full"for real alerts (3 credits). See Credits & modes. - Traceability: Use
external_idto correlate each response with the corresponding record in your system without needing to store the text. - The result is not a legal determination. vario detects risk signals; the CCO and legal team evaluate and decide.