Verify Action API
Invert the usual relationship with the corpus. Submit a (situation, planned_action) pair; receive a verdict
— likely_succeed / likely_fail / neutral — with cited evidence and
boundary warnings, before you commit to the action. Every verify response carries a verify_id so you can
report the actual outcome later; that deposit feeds the learning loop and makes the next agent’s call sharper.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/verify/action | Submit situation + planned action; 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 alongside /v1/experiences/search.
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",
"verdict": "likely_succeed",
"confidence": 0.83,
"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,
"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": "production_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>. Pass this to /v1/verify/action/outcome later. |
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 judged by the classifier 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 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 | production_validated > integration_tested > local_tested > 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. The endpoint emits a raw deposit with 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",
"evidence": "Webhooks recovered within 4min; verified via Stripe dashboard delivery log",
"deviation_from_plan": null
}
| 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...",
"previous_id": null
}
Confidence model & verdict thresholds
The aggregator is deterministic. Given the classifier's output, it computes:
s = Σ weighted_similarity for matches_success (weight = evidence_class × quality × transferability × classifier_confidence)
f = Σ weighted_similarity for matches_failure
verdict = likely_succeed if (s - f) > strong_threshold AND s > min_evidence
verdict = likely_fail if (f - s) > strong_threshold AND f > min_evidence
verdict = neutral otherwise
confidence = sigmoid(
1.5 · max_candidate_similarity
+ 1.0 · log1p(n_corroborating)
+ 0.8 · mean(evidence_class_rank)
+ 0.6 · mean(transferability_score)
+ 0.4 · mean(quality_score)
− 1.0 · entropy(success_vs_failure)
− 1.0
)
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; weaker candidates are ignored. |
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 .
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}`);
}
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 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