---
title: 2. Prove it in SIMULATE
description: Real refusals, zero side effects. The mode where a wrong policy costs you a wrong line in a report.
label: Prove it in SIMULATE
---

`SIMULATE` runs the complete pipeline and executes nothing. No credential is
brokered, no downstream call is made, no external side effect occurs anywhere.
Refusals are real refusals — your agent sees them and has to handle them.

This is where policy correctness gets settled, because a mistake here costs you
a wrong line in a report instead of a wrong row in a database.

## Switch to it

```bash
veefore deploy --mode SIMULATE --reason "Validating refund policy from observed traffic"
```

## What to do

:::steps

:::step Replay what you already saw

Preflight does this automatically, and it is the single most useful thing in the
promotion flow: it re-evaluates your recent observed traffic in the target mode
and tells you exactly which calls start being refused.

You can also do it directly for a specific shape of request:

```bash
curl -X POST https://veefore.example.com/v1/policies/simulate \
  -H "Authorization: Bearer $VEEFORE_API_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "agentId": "agt_01JAV…",
    "tool": "refund_customer",
    "capability": "refund.create",
    "amount": 150000
  }'
```

The response carries the decision and the full explanation: the policy, the
version, the deciding rule, every rule that matched, the resolved authority and
the risk signals. Simulation has no side effects of any kind — it does not even
write a decision record, because a decision that did not happen does not belong
in the audit trail.
:::

:::step Run the policy test cases

```bash
veefore policies test
```

These are the Given/Then cases you recorded during `OBSERVE`. They must all
pass; activation refuses while any of them fails, and there is no override flag.
:::

:::step Make your agent handle refusals properly

This is the step teams skip and then regret. In `OBSERVE` your agent never saw a
refusal, so its error handling has never been exercised. In `SIMULATE` it will
be.

```ts
import {
  VeeforeBlockedError,
  VeeforeApprovalRequiredError,
  VeeforeAuthenticationError,
} from '@veefore/sdk';

try {
  await agent.act({ tool: 'refund_customer', amount: 250_00, currency: 'INR' });
} catch (error) {
  if (error instanceof VeeforeApprovalRequiredError) {
    // Expected. Hand back to a human and resume later.
    return { status: 'awaiting_approval', approvalId: error.approvalId };
  }
  if (error instanceof VeeforeBlockedError) {
    // Expected. Report the reason; never retry a policy refusal.
    return { status: 'refused', reason: error.message };
  }
  if (error instanceof VeeforeAuthenticationError) {
    // Not expected. Identity is wrong — fail loudly.
    throw error;
  }
  throw error;
}
```

The important distinction: a refusal is an **answer**, not a transport failure.
Retrying it produces the same answer and burns your rate limit.
[Errors and retries](/build/errors-and-retries) has the full table.
:::

:::step Exercise the approval path end to end

If any rule returns `REQUIRE_APPROVAL`, walk the whole loop at least once:

1. The agent requests the action and gets `APPROVAL_REQUIRED`.
2. An approval appears in the console queue with full context.
3. A real person approves it.
4. The agent retries with the `approvalId` and proceeds.

Then deny one, and check the agent reports the denial reason usefully rather
than logging "request failed".

:::warning Check someone is actually watching the queue
`ENFORCE` with approval rules and an unwatched queue is just a slower kind of
blocked. Decide who owns the queue, what the response-time expectation is, and
what happens out of hours — before you promote, not after the first approval
times out.
:::
:::

:::step Clear doctor

```bash
veefore doctor --strict
```

`--strict` fails on warnings too. Fix all of it. The findings that matter most
here:

- A dangerous tool permitted only by a default decision, with no rule covering it
- Trust evidence that has expired
- Credentials overdue for rotation
- An agent with no policy bound to its scope at all
:::

:::

## What SIMULATE will not tell you

**Whether your downstream accepts the request.** Nothing is executed, so a
credential with the wrong material key, a destination that resolves differently
from production, or a downstream that rejects the payload all look fine here.
Those surface on the first real `ENFORCE` execution.

The mitigation is to make the first `ENFORCE` deployment small — see
[3. Turn on ENFORCE](/journey/enforce).

**Timing and load behaviour.** No execution means no downstream latency and no
timeouts.

## Ready when

- Every policy test case passes.
- Preflight replay shows only refusals you intended.
- Your agent handles `BLOCK`, `REQUIRE_APPROVAL` and `APPROVAL_DENIED` without crashing or retrying.
- The approval loop has been walked by a real person.
- `veefore doctor --strict` is clean.
- The approval queue has a named owner.

## Next

:::cards

:::card 3. Turn on ENFORCE | /journey/enforce
The promotion, and how to make the first one small.
:::

:::card Errors and retries | /build/errors-and-retries
Exactly which refusals to handle and how.
:::

:::
