# Request and Response Capture

<EnterpriseFeature name="Request & Response Capture" />

Request and response capture stores the complete payload of every request and
response that traverses your Zuplo gateway — the full URL, headers, body, and
timing — so your team can investigate incidents, satisfy compliance audits, and
replay traffic against new gateway configurations.

Capture is built for the moments when sampled traces and aggregated metrics
aren't enough: a single failing customer call, a regulator asking what data left
the perimeter on a specific day, or a post-incident review that needs the exact
bytes that crossed the wire.

## Overview

When capture is enabled on a project or route, Zuplo records:

- **Request**: HTTP method, full URL (path + query string), headers, and body
- **Response**: status code, headers, and body
- **Context**: client IP, user agent, geolocation, request ID, route, and policy
  execution timings
- **Identity**: authenticated subject (user, API key, consumer) when available

Captured payloads are stored in encrypted object storage in your selected region
and indexed for search by request ID, route, status code, consumer, time range,
and any custom tag your policies emit.

:::note

Capture is an Enterprise add-on. Contact your account team to enable it on a
project, configure retention, or scope capture to a subset of routes.

:::

## Sensitive Data Filtering

Raw request and response bodies routinely carry data that you cannot store —
bearer tokens, customer PII, payment details, health information. Capture
applies a configurable filter pipeline before the payload is written to storage,
so the on-disk record is already sanitised.

Out of the box, the filter redacts:

- **Authentication headers**: `Authorization`, `Cookie`, `Set-Cookie`,
  `X-Api-Key`, `Proxy-Authorization`
- **Common credential fields**: `password`, `token`, `secret`, `apiKey`,
  `client_secret`, `refresh_token`, `access_token`
- **Payment data**: PAN-shaped strings (Luhn-validated), CVV fields
- **PII heuristics**: email addresses, US/EU phone numbers, SSNs, IBANs

You can extend the filter with:

- **JSONPath redactions** for known sensitive fields in your API contracts
- **Regex patterns** for project-specific identifiers
- **Per-route policy** to disable capture or capture-with-no-body for endpoints
  that handle the most sensitive data
- **Allow-lists** so you only capture fields you've explicitly approved (the
  safest default for regulated industries)

```json title="config/request-capture.json"
{
  "$schema": "https://cdn.zuplo.com/schemas/request-capture.json",
  "enabled": true,
  "retentionDays": 30,
  "sampling": {
    "errors": 1.0,
    "success": 0.1
  },
  "filters": {
    "headers": {
      "redact": ["authorization", "cookie", "x-api-key", "x-internal-secret"]
    },
    "bodyJsonPath": {
      "redact": [
        "$.password",
        "$.user.ssn",
        "$.payment.card.number",
        "$.payment.card.cvv",
        "$..token"
      ]
    },
    "bodyRegex": [
      {
        "pattern": "\\b\\d{3}-\\d{2}-\\d{4}\\b",
        "replacement": "[REDACTED-SSN]"
      }
    ]
  },
  "scope": {
    "excludeRoutes": ["/internal/*", "/webhooks/stripe"]
  }
}
```

## Enabling Capture

Capture is configured per-project. To enable it:

1. Open
   [**Project Settings → Request Capture**](https://portal.zuplo.com/+/account/project/settings/request-capture)
   in the Zuplo Portal.
2. Toggle capture on and select a retention period (7, 30, 90, or 365 days, or a
   custom value defined in your enterprise agreement).
3. Choose a sampling strategy. Capturing 100% of production traffic is
   supported, but most teams sample errors at 100% and successes at 1–10%.
4. Configure the sensitive-data filter. Start with the defaults and add
   project-specific JSONPath or regex rules.
5. Scope capture to specific routes, environments, or consumers if needed.

Changes take effect on the next deployment. Capture can be paused at any time
without losing historical data.

## Searching and Replaying Captures

Captured requests are searchable from the
[**Request Explorer**](https://portal.zuplo.com/+/account/project/requests) in
the Zuplo Portal. Common workflows:

- **Debug a customer ticket**: paste the customer-supplied request ID to pull
  the full request, response, and policy timeline for that single call.
- **Investigate a 5xx spike**: filter to `status >= 500` over the spike window
  and group by route to see which handler is failing.
- **Replay against a new gateway version**: select a window of captured traffic
  and replay it against a preview environment to validate a policy or backend
  change before promoting it.
- **Compliance evidence**: export a filtered slice as a signed JSONL bundle for
  auditors.

## API Access

Captures can be queried programmatically:

```bash
curl -X GET \
  "https://dev.zuplo.com/accounts/{account}/projects/{project}/captures" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  --data-urlencode "startTime=2026-01-01T00:00:00Z" \
  --data-urlencode "endTime=2026-01-02T00:00:00Z" \
  --data-urlencode "status=500-599" \
  --data-urlencode "route=/v1/payments"
```

### Common Query Parameters

- `startTime` / `endTime` — ISO 8601 timestamps (max 24-hour window per query)
- `status` — single code (`429`) or range (`500-599`)
- `route` — route pattern, supports glob (`/v1/payments/*`)
- `consumer` — API key consumer ID or subject
- `requestId` — exact request ID for a single capture
- `tag` — custom tag emitted by a policy via
  `context.captureTags.add("trace-id", traceId)`

Each capture is returned as a JSON document; the full request and response
bodies are fetched separately as signed URLs so large payloads don't bloat list
responses.

## Compliance Considerations

Request and response capture is intended to be the system of record for "what
data crossed our API." A few practices to keep it audit-ready:

- **Define your filter rules in code review.** Treat
  `config/request-capture.json` like any other security control — require
  approval to change which fields are redacted.
- **Set retention to your statutory minimum.** Most regulations require 90 days
  to 7 years of evidence. Configure retention once and let Zuplo enforce the
  lifecycle.
- **Restrict portal access with RBAC.** The
  [Audit Logs](./accounts/audit-logs.mdx) feature records every capture query,
  including who ran it and what filter they used, so a reviewer can reconstruct
  who saw what.
- **Use sampling for hot routes.** Health checks and high-volume read paths
  rarely add forensic value — sampling them at 1% keeps your bill and your
  storage footprint sensible.

:::tip

For lighter-weight logging that doesn't require captured bodies, use the
[OpenTelemetry tracing](./opentelemetry.mdx) built into every Zuplo gateway, or
compose a [custom logging policy](./log-request-response-data.mdx).

:::

## Limitations

- Maximum captured body size is 10 MB per request and 10 MB per response by
  default. Larger payloads are truncated and flagged. Higher limits available on
  request.
- Streaming responses (Server-Sent Events, chunked transfer) are captured up to
  the body size limit; the full stream is summarised with byte count and
  duration.
- Captures are eventually consistent — typical end-to-end latency from request
  completion to searchable storage is under 60 seconds.
- Capture cannot be applied retroactively to traffic that occurred before the
  feature was enabled.
