Skip to main content
An interceptor is a transparent proxy. Specter sits inline between your client and a destination — the acquirer, PSP, or any other payment endpoint you would otherwise call directly: it receives the request body, maps it into a standard Decision API request on the built-in transaction schema, evaluates it, and either forwards the original request unchanged (ALLOW) or returns a configured response (BLOCK / REVIEW) without contacting the destination. The evaluation is identical to the API pattern — the same rulesets, backends, and outcomes. Only the transport differs: you point the destination URL at Specter instead of calling the Decision API yourself, so no decision logic is needed in your backend.

Runtime flow

1

Map

Specter applies the interceptor’s field_mapping to the inbound body to build a Decision API request.
2

Decide

The decision request is evaluated against the active ruleset for its context.
3

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.
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.
1

Passthrough

Activate the interceptor with no 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.
2

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.
3

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.
4

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.

Transparent interceptor (shadow)

Specter observes but never blocks: every request reaches the destination, and the decision rides along in the response header.
  1. The merchant sends the request to the destination through the Specter interceptor.
  2. Specter engages backends in shadow mode.
  3. Specter forwards the request to the destination.
  4. The destination returns the result.
  5. Specter returns the destination’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.
  1. The merchant sends the request to the destination through the Specter interceptor.
  2. Specter engages backends.
  3. Specter forwards the request to the destination only if the decision outcome is ALLOW.
  4. The destination returns the result.
  5. Specter returns the destination’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.

Field mapping

Each entry builds part of the Decision API request — the target names a field of the built-in transaction schema (transaction.amount, credential.type, …). Entries are evaluated in order and merged.
Interceptors map onto the built-in transaction schema only; the context an interceptor names must resolve to a transaction-bound ruleset. This is enforced at both ends: creating or updating an interceptor rejects with 422 when a target is not declared by the transaction schema (dynamic metadata.<key> targets are accepted, although the editor’s target list does not enumerate them) or when the named context currently resolves to a custom-bound ruleset — such an interceptor could never execute. A context with no active ruleset yet is accepted, since the binding is not knowable at that point; if it later resolves custom-bound, executing the interceptor refuses with 502 INTERCEPTOR_BINDING_UNSUPPORTED naming the context and the bound schema. Passthrough interceptors (empty mapping) never touch the binding and are not refused.
Source paths support $ (root) and $.a.b.c dot notation. Missing paths produce no output — partial mappings are valid. Add "sensitive": true to a row whose value must not appear in the payload copy stored on the decision; the copy is otherwise redacted by the classification markers of the schema, never by guessing from a source field’s name.

Filters

Filters chain with | (e.g. "downcase | first(6)"). These are mapping filters, distinct from the filter() pipes of rule conditions:

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.
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 destination credentials without exposing them in the body.
  • Appends content-type: application/json and x-specter-decision-id: <uuid>.
The destination response (headers, body, status) is returned to the caller unchanged.

API

The direct-call alternative when you control the payment backend.