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

# Via Interceptor

> Run fraud checks inline as a transparent proxy in front of your acquirer.

An **interceptor** is a transparent proxy. Specter sits inline between your client and the acquirer: it receives
the acquirer request body, **maps it into a standard [Decision API](/products/specter/integration/via-api)
request**, evaluates that request, and either forwards the original request unchanged (`ALLOW`) or returns a
configured response (`BLOCK` / `REVIEW`) without contacting the acquirer.

The evaluation is identical to the [API pattern](/products/specter/integration/via-api) — the same rulesets,
backends, and outcomes. Only the transport differs: you point your acquirer URL at Specter instead of calling
the Decision API yourself, so no fraud logic is needed in your backend.

## Runtime flow

```
POST /api/interceptors/{ref}
Authorization: Bearer <token with interceptors:execute>
Content-Type: application/json

<acquirer request body>
```

<Steps>
  <Step title="Map">
    Specter applies the interceptor's `field_mapping` to the inbound body to build a
    [Decision API request](/products/specter/integration/via-api#request).
  </Step>

  <Step title="Decide">
    The decision request is evaluated against the active ruleset for its
    context.
  </Step>

  <Step title="Forward or respond">
    `ALLOW` → forward the original body unchanged to the destination and stream
    the response back. `BLOCK` / `REVIEW` → return the configured response
    without calling the destination.
  </Step>
</Steps>

The decision is logged with `integration: "interceptor"` and the interceptor reference.

## Progressive rollout

The interceptor is built to be adopted in stages, so you can de-risk each step before it touches live traffic.
Each stage adds one capability on top of the last.

<Steps>
  <Step title="Passthrough">
    Activate the interceptor with no [field mapping](#field-mapping). Specter
    forwards every request unchanged and auto-discovers the fields it sees,
    giving you the data to build the mapping. No decision is made, and no
    `x-specter-decision-id` header is added.
  </Step>

  <Step title="Mapping">
    Add the field mapping. Specter now builds and validates a decision request
    from each inbound body, so you can confirm the request is understood. With
    no blocking rules yet, traffic still flows through untouched.
  </Step>

  <Step title="Shadow decisions">
    Add rules and backends in **shadow mode** (`live: false`). Specter evaluates
    and logs the decision and returns its ID in the `x-specter-decision-id`
    header, but shadow rules never change the outcome — so every request is
    still forwarded. You measure decision quality against real traffic with zero
    customer impact.
  </Step>

  <Step title="Active">
    Switch the rules and backends to live. Specter now enforces the decision: it
    forwards on `ALLOW` and returns the configured response on `BLOCK` /
    `REVIEW`.
  </Step>
</Steps>

### Transparent interceptor (shadow)

Specter observes but never blocks: every request reaches the acquirer, and the decision rides along in the
response header.

```mermaid theme={null}
sequenceDiagram
    participant M as Merchant
    participant S as Specter
    participant B as Risk backend
    participant A as Acquirer
    M->>S: Acquirer request
    S->>B: Evaluate in shadow mode
    S->>A: Forward request
    A-->>S: Result
    S-->>M: Result + decision in header
```

1. The merchant sends the request to the acquirer through the Specter interceptor.
2. Specter engages backends in shadow mode.
3. Specter forwards the request to the acquirer.
4. The acquirer returns the result.
5. Specter returns the acquirer's exact result, with the decision information in the response header.

### Active interceptor

Once you trust the decisions, switch to live. Specter forwards only approved requests; everything else is
short-circuited with your configured response.

```mermaid theme={null}
sequenceDiagram
    participant M as Merchant
    participant S as Specter
    participant B as Risk backend
    participant A as Acquirer
    M->>S: Acquirer request
    S->>B: Evaluate
    alt decision is ALLOW
        S->>A: Forward request
        A-->>S: Result
        S-->>M: Result + decision in header
    else BLOCK / REVIEW
        S-->>M: Configured response
    end
```

1. The merchant sends the request to the acquirer through the Specter interceptor.
2. Specter engages backends.
3. Specter forwards the request to the acquirer only if the decision outcome is `ALLOW`.
4. The acquirer returns the result.
5. Specter returns the acquirer's exact result, with the decision information in the response header.

## Configuration

Interceptors are configured through the Admin API (scopes `admin:interceptors:read` / `admin:interceptors:write`)
and have their own `DRAFT → ACTIVE` lifecycle with activate and rollback.

```json theme={null}
{
  "description": "Adyen checkout interceptor",
  "active": true,
  "destination": {
    "url": "https://checkout-test.adyen.com/v70/payments",
    "timeout_ms": 5000
  },
  "field_mapping": [
    { "source": "$.amount.value", "target": "transaction.amount" },
    { "source": "$.amount.currency", "target": "transaction.currency" },
    { "source": "$.reference", "target": "transaction.reference" },
    { "source": "$.shopperIP", "target": "device.ip" },
    {
      "source": "$.paymentMethod.encryptedCardNumber",
      "filter": "first(6)",
      "target": "credential.first_six"
    },
    {
      "source": "$.paymentMethod.encryptedCardNumber",
      "filter": "last(4)",
      "target": "credential.last_four"
    },
    { "const": "masked_pan", "target": "credential.type" }
  ],
  "responses": {
    "block": {
      "mode": "template",
      "status_code": 422,
      "content_type": "application/json",
      "body": {
        "message": "Request blocked by fraud rules",
        "reference": "{{ decision_id }}"
      }
    },
    "review": {
      "mode": "decision"
    }
  }
}
```

## Field mapping

Each entry builds part of the [Decision API request](/products/specter/integration/via-api#request) — the
`target` names a field of that request (`transaction.amount`, `credential.type`, …). Entries are evaluated in
order and merged.

| Entry           | Example                                                                         | Effect                              |
| --------------- | ------------------------------------------------------------------------------- | ----------------------------------- |
| Source          | `{ "source": "$.amount.value", "target": "transaction.amount" }`                | Copy a value from the inbound body. |
| Const           | `{ "const": "masked_pan", "target": "credential.type" }`                        | Assign a static value.              |
| Source + filter | `{ "source": "$.pan", "filter": "first(6)", "target": "credential.first_six" }` | Transform before assigning.         |

Source paths support `$` (root) and `$.a.b.c` dot notation. Missing paths produce no output — partial mappings are valid.

### Filters

Filters chain with `|` (e.g. `"downcase | first(6)"`):

| Filter       | Description                           |
| ------------ | ------------------------------------- |
| `first(n)`   | First `n` characters.                 |
| `last(n)`    | Last `n` characters.                  |
| `tail(n)`    | All characters after position `n`.    |
| `map({...})` | Map a value via a lookup table.       |
| `to_int`     | Parse a numeric string to an integer. |
| `downcase`   | Lowercase the string.                 |

## Responses

Each outcome key (`block`, `review`) maps to a response config:

* **`decision` mode** — returns the standard Specter decision map as-is.
* **`template` mode** — returns a static body with placeholders resolved at runtime.

| Placeholder                    | Resolves to                               |
| ------------------------------ | ----------------------------------------- |
| `{{ decision_id }}`            | UUID of the logged decision               |
| `{{ decision }}`               | `BLOCK` or `REVIEW`                       |
| `{{ source }}`                 | Rule source (backend name or `condition`) |
| `{{ credential_fingerprint }}` | Credential fingerprint                    |
| `{{ latency_ms }}`             | Evaluation latency in milliseconds        |
| `{{ triggered_rules }}`        | JSON array of triggered rule IDs          |

When no response is configured for an outcome, Specter defaults to `decision` mode.

## Header forwarding (ALLOW)

On `ALLOW`, Specter forwards the request to the destination and:

* Strips reserved headers: `authorization`, `content-length`, `content-type`, `host`.
* Resolves `x-{label}-name` / `x-{label}-value` header pairs into a single `{name}: {value}` header — used to
  inject acquirer credentials without exposing them in the body.
* Appends `content-type: application/json` and `x-specter-decision-id: <uuid>`.

The acquirer response (headers, body, status) is returned to the caller unchanged.

## Related

<Card title="API" icon="code" href="/products/specter/integration/via-api">
  The direct-call alternative when you control the payment backend.
</Card>
