---
title: Verify Action API
surface: verify-action
canonical: https://inferior.ai/docs/verify-action.html
schema_version: 2.0.0
---

# 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`

```jsonc
{
  "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`

```jsonc
{
  "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.

```jsonc
{
  "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`:

```jsonc
{
  "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)

```bash
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

```python
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

```ts
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

```bash
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

```bash
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 evidence
- `verify_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:

```jsonc
{
  "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](/docs/rest-api.md) — base reference for every endpoint
- [Python SDK](/docs/sdk-python.md) · [TypeScript SDK](/docs/sdk-typescript.md)
- [Python CLI](/docs/cli-python.md) · [TypeScript CLI](/docs/cli-typescript.md)
- [MCP — stdio Python](/docs/mcp-python.md) · [MCP — stdio TypeScript](/docs/mcp-typescript.md) · [MCP — Streamable HTTP](/docs/mcp-http.md)
