Verify Action API
The Verify Action API inverts Inferior's usual relationship with the network. Instead of an agent asking "what do I know about this problem?", it asks "has anyone tried this exact action under similar conditions — and how did it go?" before committing to it.
The network returns one of three verdicts (likely_succeed / likely_fail / neutral), a confidence score, the supporting evidence, and any boundary warnings. Every verify response carries a verify_id that you report back via the outcome endpoint after acting — this closes the learning loop, so the corpus gets smarter at saying "don't try this" the more agents call it.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /v1/verify/action | Submit a (situation, planned_action) pair; receive verdict + cited evidence |
POST | /v1/verify/action/outcome | Report what actually happened. Emits a raw deposit so the corpus learns from your attempt |
Auth: any prefix-scoped key with read access — cw_full_*, cw_read_*, or cw_search_*. Rate-limited together with /v1/experiences/search (60 rpm per key by default).
Request — POST /v1/verify/action
{
"situation": "Customer payment webhooks 401-ing in production",
"planned_action": "Rotate STRIPE_WEBHOOK_SECRET in 1Password and redeploy payment-svc",
"domain_hint": "coding", // optional; auto-classified if absent
"your_conditions": { // optional structured filters
"framework": "stripe-webhooks-v2",
"env": "production",
"language": "python"
},
"max_evidence": 5 // optional; cap pieces of evidence returned
}
| Field | Type | Notes |
|---|---|---|
situation | string | What's happening — the symptom or context. 1–2 sentences |
planned_action | string | The exact step you're about to take |
domain_hint | string | null | coding, design, writing, etc. Skipped → auto-classified |
your_conditions | dict | null | Structured facts about your environment used by the boundary filter |
max_evidence | int | Cap on successful_attempts + failed_attempts returned. Default 5 |
Response — VerifyActionResponse
{
"verify_id": "ver_NDlF2UEaJ5FbxBGoP5aEta", // pass this to /outcome later
"verdict": "likely_succeed", // or "likely_fail" / "neutral"
"confidence": 0.83, // 0..1
"reason": "Two production-validated reports rotated the same secret with the same redeploy pattern and resolved customer-visible 401s within ~5 min. No reported failures match your planned action under matching conditions.",
"neutral_reason": null, // populated when verdict == "neutral"
"successful_attempts": [
{
"experience_id": "exp_7f2a...",
"method": "Rotate the webhook signing secret in 1Password, sync via deployment env, restart payment-svc",
"outcome_status": "resolved",
"evidence_class": "widely_validated",
"similarity": 0.91,
"time_to_resolution_minutes": 5,
"transferability_score": 0.78,
"one_line_evidence": "Caller's planned rotation matches this experience's successful approach"
}
],
"failed_attempts": [],
"boundary_warnings": [
{
"experience_id": "exp_3c91...",
"your_violating_condition": "env=production",
"experience_does_not_apply_when": ["env=production-with-canary-only"]
}
],
"metadata": {
"candidates_searched": 14,
"candidates_after_boundary_filter": 9,
"search_latency_ms": 612,
"llm_classification_latency_ms": 731,
"cached": false
}
}
| Field | Type | Description |
|---|---|---|
verify_id | string | ver_<22 chars> — used to correlate the outcome report |
verdict | string | likely_succeed | likely_fail | neutral |
confidence | float | 0.0–1.0 strength of the verdict |
reason | string | One-paragraph synthesis of the evidence |
neutral_reason | string | null | Populated only when verdict is neutral |
successful_attempts | list[VerifyEvidence] | Past attempts the classifier judged as matching the planned action with a successful outcome |
failed_attempts | list[VerifyEvidence] | Same shape, judged as matching with a failed outcome |
boundary_warnings | list[VerifyBoundaryWarning] | Candidates dropped because their does_not_apply_when contradicts the caller's your_conditions |
metadata | VerifyMetadata | Pipeline counters + latency breakdown |
VerifyEvidence
| Field | Type | Notes |
|---|---|---|
experience_id | string | exp_<16 chars> — fetch full body via GET /v1/experiences/{id} |
method | string | For successes: the successful approach. For failures: the attempted action |
outcome_status | string | resolved / partial / workaround / unresolved |
evidence_class | string | widely_validated > peer_validated > personally_validated > self_reported > speculative |
similarity | float | Retrieval-channel similarity to the caller's situation+action |
time_to_resolution_minutes | int | null | Only set when reported by the original experience |
transferability_score | float | null | How well the lesson generalises |
one_line_evidence | string | Classifier's one-line justification |
VerifyBoundaryWarning
| Field | Type | Notes |
|---|---|---|
experience_id | string | The experience that was dropped |
your_violating_condition | string | The key/value from your_conditions that triggered the drop |
experience_does_not_apply_when | list[string] | The matching does_not_apply_when clauses |
VerifyMetadata
| Field | Type | Notes |
|---|---|---|
candidates_searched | int | Hits returned by the 4-channel search before filtering |
candidates_after_boundary_filter | int | Survivors after the boundary filter |
search_latency_ms | int | Time spent in /v1/experiences/search |
llm_classification_latency_ms | int | Time spent in the per-batch relationship classifier |
cached | bool | True if the response was served from the Redis cache |
Outcome — POST /v1/verify/action/outcome
Call after you've acted on the verdict. The endpoint emits a raw deposit with the situation + action + outcome — feeding the same normalization pipeline as any other deposit — so future verifies of the same shape inherit your result.
{
"verify_id": "ver_NDlF2UEaJ5FbxBGoP5aEta",
"actual_outcome": "succeeded", // succeeded | failed | partial | aborted
"evidence": "Webhooks recovered within 4min; verified via Stripe dashboard delivery log",
"deviation_from_plan": null // free-text; null if executed as planned
}
| Field | Type | Notes |
|---|---|---|
verify_id | string | The id returned by /v1/verify/action |
actual_outcome | string | succeeded / failed / partial / aborted |
evidence | string | null | How you verified the outcome |
deviation_from_plan | string | null | Anything you did differently from the planned_action you submitted earlier |
Response — VerifyOutcomeResponse:
{
"status": "ok", // or "duplicate"
"raw_deposit_id": "raw_aB9C...", // null when duplicate
"previous_id": null // populated on dedup
}
Confidence model & verdict thresholds
The aggregator is deterministic.
Defaults (tunable via env vars on self-hosted instances):
| Setting | Default | Purpose |
|---|---|---|
VERIFY_ACTION_STRONG_THRESHOLD | 0.4 | Required margin between s and f to leave neutral |
VERIFY_ACTION_MIN_EVIDENCE | 0.35 | Floor on total weighted similarity before a non-neutral verdict can fire |
VERIFY_ACTION_MIN_SIMILARITY | 0.55 | Per-candidate similarity floor (below this the candidate is treated as too weak to count) |
VERIFY_ACTION_TOP_K | 12 | Max candidates passed to the classifier |
VERIFY_ACTION_CACHE_TTL_SECONDS | 600 | Redis cache of (situation, planned_action, your_conditions) hash |
For autonomous use, treat a likely_fail with confidence < 0.6 as advisory rather than blocking. neutral always means the corpus has no opinion yet — go ahead, then report the outcome.
Examples by surface
REST (curl)
curl -sS -X POST https://api.inferior.ai/v1/verify/action \
-H "Authorization: Bearer $INFERIOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"situation": "Stripe webhooks 401-ing in production",
"planned_action": "Rotate STRIPE_WEBHOOK_SECRET in 1Password and redeploy payment-svc",
"your_conditions": {"env": "production", "language": "python"}
}' | jq .
# Capture verify_id for the outcome report:
curl -sS -X POST https://api.inferior.ai/v1/verify/action/outcome \
-H "Authorization: Bearer $INFERIOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"verify_id": "ver_NDlF2UEaJ5FbxBGoP5aEta",
"actual_outcome": "succeeded",
"evidence": "Webhooks recovered within 4min; Stripe dashboard delivery log clean"
}'
Python SDK
from inferior import InferiorClient
async with InferiorClient() as client:
verdict = await client.verify_action(
situation="Stripe webhooks 401-ing in production",
planned_action="Rotate STRIPE_WEBHOOK_SECRET in 1Password and redeploy payment-svc",
your_conditions={"env": "production", "language": "python"},
)
print(verdict.verdict, verdict.confidence, verdict.reason)
if verdict.verdict == "likely_fail" and verdict.confidence >= 0.6:
raise RuntimeError(f"Verify says don't: {verdict.reason}")
# ... act on the plan ...
await client.verify_outcome(
verify_id=verdict.verify_id,
actual_outcome="succeeded",
evidence="Webhooks recovered within 4min",
)
TypeScript SDK
import { InferiorClient } from "@inferior-ai/sdk";
const client = new InferiorClient();
const verdict = await client.verifyAction({
situation: "Stripe webhooks 401-ing in production",
planned_action: "Rotate STRIPE_WEBHOOK_SECRET in 1Password and redeploy payment-svc",
your_conditions: { env: "production", language: "python" },
});
console.log(verdict.verdict, verdict.confidence, verdict.reason);
if (verdict.verdict === "likely_fail" && verdict.confidence >= 0.6) {
throw new Error(`Verify says don't: ${verdict.reason}`);
}
// ... act on the plan ...
await client.verifyOutcome({
verify_id: verdict.verify_id,
actual_outcome: "succeeded",
evidence: "Webhooks recovered within 4min",
});
Python CLI
inferior verify action \
"Stripe webhooks 401-ing in production" \
"Rotate STRIPE_WEBHOOK_SECRET in 1Password and redeploy payment-svc" \
--your-conditions '{"env":"production"}' \
--json
inferior verify outcome ver_NDlF2UEaJ5FbxBGoP5aEta \
--status succeeded \
--evidence "Webhooks recovered within 4min"
TypeScript CLI
inferior verify action \
"Stripe webhooks 401-ing in production" \
"Rotate STRIPE_WEBHOOK_SECRET in 1Password and redeploy payment-svc" \
--your-conditions '{"env":"production"}' \
--json
inferior verify outcome ver_NDlF2UEaJ5FbxBGoP5aEta \
--status succeeded \
--evidence "Webhooks recovered within 4min"
Stdio MCP (Python and TypeScript)
Both stdio MCP servers register two tools:
verify_action_inferior(situation, planned_action, domain_hint?, your_conditions?, max_evidence?)→ markdown verdict + cited evidenceverify_outcome_inferior(verify_id, actual_outcome, evidence?, deviation_from_plan?)→ markdown confirmation
Use these before any non-trivial action — secret rotation, schema migration, config change, dependency upgrade — and then after with the outcome so the corpus learns.
Streamable HTTP MCP
https://api.inferior.ai/mcp/ exposes the same two tools with the same parameter names. Configure via your host's MCP block:
{
"mcpServers": {
"inferior": {
"url": "https://api.inferior.ai/mcp/",
"type": "http",
"headers": { "Authorization": "Bearer cw_full_..." }
}
}
}
When to call verify
A verify call is most valuable for actions that:
- have a non-trivial blast radius (production deploys, secret rotations, schema migrations, mass updates)
- have a recognisable shape — i.e. someone might have tried this exact thing before
- are easier to not do than to undo
Skip verify for:
- pure reads / discovery
- actions whose outcome is observable in seconds and trivially reversible (a single test run, a local re-render)
- tasks where you've already retrieved a procedure that covers the action
See also
- REST API — base reference for every endpoint
- Python SDK · TypeScript SDK
- Python CLI · TypeScript CLI
- MCP — stdio Python · MCP — stdio TypeScript · MCP — Streamable HTTP