> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clevis.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Magic Triggers

> Force deterministic outcomes for demos and tests

The mock provider exposes **magic trigger values** that pick a deterministic
outcome at intake. Use them to drive demos and tests through any path —
happy, rejection, stuck, or slow — without timing flakiness.

<Warning>
  These are **dev/test affordances**, not stable API behaviour. They are
  available on the mock provider while sandbox is in place.
</Warning>

## Order of evaluation

Triggers are checked in order — **first match wins**:

<Steps>
  <Step title="X-Mock-Outcome header">
    If present and recognised, the header overrides everything else (including
    body triggers).
  </Step>

  <Step title="external_id prefix">
    `MOCK_REJECT_INTAKE_`, `MOCK_REJECT_BANK_`, `MOCK_SLOW_`, `MOCK_STUCK_`, `MOCK_OK_`.
  </Step>

  <Step title="amount suffix">
    `.13` and `.99` (after rounding to 2dp).
  </Step>

  <Step title="document_id">
    The literal value `"00000000000"` triggers a compliance-screening reject.
  </Step>

  <Step title="Default">
    Happy path: `PENDING → PROCESSING → PAID`.
  </Step>
</Steps>

## `external_id` prefixes

| Prefix                | Outcome                                        | `status_code` | `status_detail` ends with                                |
| --------------------- | ---------------------------------------------- | ------------- | -------------------------------------------------------- |
| `MOCK_OK_`            | Happy path (explicit; same as default)         | `"200"`       | `Payout received and queued.`                            |
| `MOCK_REJECT_INTAKE_` | Reject **at intake** — no `PROCESSING` step    | `"300"`       | `Rejected at intake (mock trigger).`                     |
| `MOCK_REJECT_BANK_`   | `PENDING → PROCESSING → REJECTED`              | `"301"`       | `Beneficiary bank rejected the transfer (mock trigger).` |
| `MOCK_SLOW_`          | Happy path forced to 30s per step              | `"200"`       | `Payout received and queued.`                            |
| `MOCK_STUCK_`         | Advances to `PROCESSING` and **never settles** | `"200"`       | `Stuck in processing (mock trigger).`                    |

<Note>
  `MOCK_SLOW_` ignores any `processing.step_seconds` override and uses 30 seconds
  per step — useful for demoing a "watch it process" UI.
</Note>

### Examples

```bash theme={null}
# Force a bank rejection
"external_id": "MOCK_REJECT_BANK_demo-001"

# Stuck in PROCESSING — for testing your "stale payout" UX
"external_id": "MOCK_STUCK_polling-demo"

# Watch it advance every 30s — for live demos
"external_id": "MOCK_SLOW_audience-demo"
```

## Amount suffixes

The mock looks at the **2-decimal string form** of `amount` (so `1000.13` →
`.13`, but `1000.1300001` also rounds to `.13` and triggers).

| Suffix | Outcome                            | `status_code` | `status_detail` ends with                              |
| ------ | ---------------------------------- | ------------- | ------------------------------------------------------ |
| `.13`  | `PENDING → PROCESSING → REJECTED`  | `"302"`       | `Insufficient funds in payout account (mock trigger).` |
| `.99`  | Reject at intake (no `PROCESSING`) | `"303"`       | `Amount exceeds per-payout limit (mock trigger).`      |

```bash theme={null}
# Demo "insufficient funds in payout account"
"amount": "1000.13"

# Demo "amount exceeds per-payout limit" (intake reject)
"amount": "1000000.99"
```

## Document ID trigger

| Field                     | Value           | Outcome          | `status_code` | `status_detail` ends with                                 |
| ------------------------- | --------------- | ---------------- | ------------- | --------------------------------------------------------- |
| `beneficiary.document_id` | `"00000000000"` | Reject at intake | `"304"`       | `Beneficiary failed compliance screening (mock trigger).` |

```bash theme={null}
"beneficiary": {
  "name": "Test User",
  "document_type": "CC",
  "document_id": "00000000000",   # compliance screen trigger
  ...
}
```

## Header overrides

The `X-Mock-Outcome` header takes precedence over everything. Combine with
`X-Mock-Outcome-At: intake` for `REJECTED` to skip the `PROCESSING` step.

| Header values                                            | Outcome                                         |
| -------------------------------------------------------- | ----------------------------------------------- |
| `X-Mock-Outcome: PAID`                                   | `PENDING → PROCESSING → PAID`                   |
| `X-Mock-Outcome: REJECTED`                               | `PENDING → PROCESSING → REJECTED` (bank reject) |
| `X-Mock-Outcome: REJECTED` + `X-Mock-Outcome-At: intake` | Reject at intake (no `PROCESSING`)              |
| `X-Mock-Outcome: CANCELLED`                              | `PENDING → PROCESSING → CANCELLED`              |

Unrecognised values are ignored (with a server-side warning log), and the
mock falls back to body triggers.

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.clevis.dev/v1/payouts \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "X-Mock-Outcome: REJECTED" \
    -H "X-Mock-Outcome-At: intake" \
    -H "Content-Type: application/json" \
    -d '{
      "amount": "100.00",
      "currency": "COP",
      "country": "CO",
      "external_id": "force-reject-001",
      "beneficiary": { ... }
    }'
  ```

  ```python Python theme={null}
  resp = httpx.post(
      "https://api.clevis.dev/v1/payouts",
      headers={
          "Authorization": "Bearer YOUR_API_KEY",
          "X-Mock-Outcome": "REJECTED",
          "X-Mock-Outcome-At": "intake",
      },
      json={
          "amount": "100.00",
          "currency": "COP",
          "country": "CO",
          "external_id": "force-reject-001",
          "beneficiary": {...},
      },
  )
  assert resp.json()["status"] == "REJECTED"
  ```

  ```javascript Node.js theme={null}
  const resp = await fetch("https://api.clevis.dev/v1/payouts", {
    method: "POST",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "X-Mock-Outcome": "REJECTED",
      "X-Mock-Outcome-At": "intake",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      amount: "100.00",
      currency: "COP",
      country: "CO",
      external_id: "force-reject-001",
      beneficiary: {/* ... */},
    }),
  });
  ```
</CodeGroup>

## Reading the trigger off the response

Every mock-triggered terminal outcome carries a `status_detail` ending in
`"(mock trigger)."` — so your tests can assert on the trigger without
hardcoding the message.

```python theme={null}
assert payout["status"] == "REJECTED"
assert payout["status_code"] == "301"
assert payout["status_detail"].endswith("(mock trigger).")
```

## Tip: synchronous + magic trigger = great for unit tests

Combine `processing.step_seconds: 0` with any trigger. The terminal state is
already applied in the create response — no polling, no timer, no flakiness.

```json theme={null}
{
  "external_id": "MOCK_REJECT_BANK_test-001",
  "amount": "100.00",
  "currency": "COP",
  "country": "CO",
  "processing": { "step_seconds": 0 },
  "beneficiary": { ... }
}
```

The response comes back with `status: "REJECTED"` and `status_code: "301"`
on the same line that creates the payout.
