---
title: Request Viewer & Audit API
description: "API endpoints for querying captured HTTP requests, downloading payload bodies, and reading workspace audit events."
---

# Request Viewer & Audit API

The Request Viewer API exposes the same data the FileRelay dashboard's **Requests** page surfaces: every HTTP request received by an ArchiveLink receiver or JCo bridge, plus the response FileRelay returned. The Audit API exposes the structured event log (e.g., `cert.activated`, `connection.created`, `document.retried`).

Together these two endpoints power debugging ("why did SAP get a 401 last Tuesday?"), compliance ("show every certificate change in March"), and post-mortem workflows.

## Captured Requests

### Object

| Field                      | Type    | Description                                                              |
|----------------------------|---------|--------------------------------------------------------------------------|
| `id`                       | string  | ULID (sortable by time, generated by connector)                          |
| `channel`                  | string  | `receiver` (ArchiveLink) or `jco` (RFC bridge)                           |
| `workspace_id`             | string  | Workspace this request belongs to                                        |
| `http_method`              | string  | `GET`, `POST`, `PUT`, `DELETE`, etc.                                     |
| `uri`                      | string  | Full request path                                                        |
| `query_string`             | string  | Raw query string                                                         |
| `request_headers`          | object  | Header map (sensitive values redacted, see [Redaction](#redaction))      |
| `request_content_type`     | string  | e.g., `application/pdf`                                                  |
| `request_content_length`   | integer | Bytes                                                                    |
| `request_body_truncated`   | boolean | `true` if the body exceeded the capture cap and was not persisted        |
| `source_ip`                | string  | Originating IP address                                                   |
| `request_at`               | string  | ISO 8601 timestamp                                                       |
| `status_code`              | integer | Response status                                                          |
| `response_headers`         | object  | Response header map                                                      |
| `response_content_type`    | string  | e.g., `application/json`                                                 |
| `response_content_length`  | integer | Bytes                                                                    |
| `response_body_truncated`  | boolean | Same as request flag                                                     |
| `duration_ms`              | integer | Total time spent handling the request                                    |
| `response_at`              | string  | ISO 8601 timestamp                                                       |
| `created_at`               | string  | Server-side insert timestamp                                             |

Binary request/response bodies are **not** returned inline. List/Get responses include flags and metadata; use the [body download endpoint](#download-a-request-or-response-body) to fetch the bytes.

### List Captured Requests

```
GET /api/{userSlug}/{workspaceSlug}/requests
```

#### Query Parameters

| Parameter     | Type    | Description                                                                | Default |
|---------------|---------|----------------------------------------------------------------------------|---------|
| `per_page`    | integer | Items per page (max 100)                                                   | 25      |
| `before`      | string  | Cursor -- return rows with `id` lexicographically less than this ULID      | --      |
| `channel`     | string  | Filter by `receiver` or `jco`                                              | --      |
| `status_code` | integer | Filter by exact response status (e.g., `401`, `500`)                       | --      |
| `status_class`| string  | Filter by class: `2xx`, `3xx`, `4xx`, `5xx`                                | --      |
| `method`      | string  | Filter by HTTP method                                                      | --      |
| `repo_id`     | string  | Filter to requests whose URI carries this `contRep`                        | --      |
| `doc_id`      | string  | Filter to requests whose URI carries this `docId`                          | --      |
| `date_from`   | string  | ISO 8601 -- include rows with `request_at >= date_from`                    | --      |
| `date_to`     | string  | ISO 8601 -- include rows with `request_at <= date_to`                      | --      |

List responses use **cursor pagination** (not offset pagination) because the underlying table grows continuously and offset pagination would be unstable. Pass the `id` of the last row back as `before` to fetch the next page.

#### Example Request

```bash
curl -X GET "https://filerelay.dev/api/acme/production/requests?channel=receiver&status_class=5xx&per_page=50" \
  -H "Authorization: Bearer fr_live_abc123def456" \
  -H "Accept: application/json"
```

#### Example Response

```json
{
  "data": [
    {
      "id": "01HM9...",
      "type": "captured_request",
      "attributes": {
        "channel": "receiver",
        "http_method": "POST",
        "uri": "/ContentServer",
        "query_string": "contRep=Z1&docId=4A3F8B2C1D7E9F0A&command=create",
        "request_content_type": "application/pdf",
        "request_content_length": 245780,
        "request_body_truncated": false,
        "source_ip": "10.20.30.40",
        "status_code": 500,
        "response_content_type": "application/json",
        "response_content_length": 132,
        "response_body_truncated": false,
        "duration_ms": 217,
        "request_at": "2026-04-04T10:29:58Z",
        "response_at": "2026-04-04T10:29:59Z"
      }
    }
  ],
  "meta": {
    "per_page": 50,
    "next_cursor": "01HM8..."
  }
}
```

When there are no more rows, `next_cursor` is `null`.

### Get a Single Captured Request

```
GET /api/{userSlug}/{workspaceSlug}/requests/:id
```

Returns the full object with both `request_headers` and `response_headers` expanded, plus an inline preview of the body when it is small text (HTML, JSON, XML under 64 KiB):

```json
{
  "data": {
    "id": "01HM9...",
    "type": "captured_request",
    "attributes": {
      "channel": "receiver",
      "http_method": "POST",
      "uri": "/ContentServer",
      "request_headers": {
        "content-type": "application/pdf",
        "x-archive-link-version": "0047",
        "authorization": "[REDACTED]"
      },
      "response_headers": {
        "content-type": "application/json",
        "x-filerelay-request-id": "01HM9..."
      },
      "request_body_preview": null,
      "response_body_preview": "{\"error\":{\"code\":\"signature_invalid\",\"message\":\"Signature does not match any active certificate.\"}}",
      "status_code": 500,
      "duration_ms": 217,
      "request_at": "2026-04-04T10:29:58Z"
    }
  }
}
```

### Download a Request or Response Body

Stream the raw body bytes with their original `Content-Type`. Useful for replaying or inspecting binary uploads.

```
GET /api/{userSlug}/{workspaceSlug}/requests/:id/body/:kind
```

`:kind` is either `request` or `response`.

#### Example Request

```bash
curl -X GET "https://filerelay.dev/api/acme/production/requests/01HM9.../body/request" \
  -H "Authorization: Bearer fr_live_abc123def456" \
  -o request.pdf
```

#### Response

The raw bytes with `Content-Type` and `Content-Disposition: attachment; filename=...` headers set. Returns `404` if the body was truncated at capture time (`*_body_truncated: true`).

### Replay a Captured Request

Re-send a previously captured request through the same processing pipeline. Useful for confirming a fix without waiting for SAP to re-trigger.

```
POST /api/{userSlug}/{workspaceSlug}/requests/:id/replay
```

Returns `202 Accepted` with the new captured-request ID:

```json
{
  "data": {
    "replay_of": "01HM9...",
    "new_request_id": "01HMA..."
  }
}
```

Requires **Admin** permissions. Only `receiver`-channel requests can be replayed -- replaying RFC calls is not supported because they are not idempotent.

## Audit Log

The audit log is the structured-event companion to the captured-request stream. It tracks workspace-level changes (who did what) rather than raw HTTP traffic.

### Object

| Field          | Type   | Description                                                                  |
|----------------|--------|------------------------------------------------------------------------------|
| `id`           | string | ULID                                                                         |
| `event`        | string | Event name, e.g., `cert.activated`, `connection.created`, `document.retried` |
| `subject_type` | string | Model class, e.g., `Certificate`, `Connection`, `Document`                   |
| `subject_id`   | string | ID of the affected resource                                                  |
| `description`  | string | Human-readable summary                                                       |
| `metadata`     | object | Event-specific structured payload                                            |
| `user_id`      | string | Workspace member who triggered the event, or `null` for system events        |
| `actor_type`   | string | `user`, `api_key`, or `system`                                               |
| `created_at`   | string | ISO 8601 timestamp                                                           |

### List Audit Events

```
GET /api/{userSlug}/{workspaceSlug}/audit-logs
```

#### Query Parameters

| Parameter      | Type    | Description                                                | Default |
|----------------|---------|------------------------------------------------------------|---------|
| `per_page`     | integer | Items per page (max 100)                                   | 25      |
| `before`       | string  | Cursor (ULID-based, same scheme as the requests endpoint)  | --      |
| `event`        | string  | Filter by exact event name                                 | --      |
| `event_prefix` | string  | Filter by event prefix, e.g., `cert.`                      | --      |
| `subject_type` | string  | Filter by subject model                                    | --      |
| `subject_id`   | string  | Filter by specific subject                                 | --      |
| `user_id`      | string  | Filter by actor                                            | --      |
| `date_from`    | string  | ISO 8601                                                   | --      |
| `date_to`      | string  | ISO 8601                                                   | --      |

#### Example Request

```bash
curl -X GET "https://filerelay.dev/api/acme/production/audit-logs?event_prefix=cert.&date_from=2026-03-01T00:00:00Z" \
  -H "Authorization: Bearer fr_live_abc123def456" \
  -H "Accept: application/json"
```

#### Example Response

```json
{
  "data": [
    {
      "id": "01HMA...",
      "type": "audit_log",
      "attributes": {
        "event": "cert.activated",
        "subject_type": "Certificate",
        "subject_id": "cert_xyz789",
        "description": "Activated certificate SAP_SYS_PRD",
        "metadata": {
          "fingerprint_sha256": "a1b2c3d4...",
          "valid_to": "2027-06-01T00:00:00Z"
        },
        "user_id": "usr_abc123",
        "actor_type": "user",
        "created_at": "2026-04-04T11:00:00Z"
      }
    }
  ],
  "meta": {
    "per_page": 25,
    "next_cursor": "01HM9..."
  }
}
```

### Export Audit Log as CSV

```
GET /api/{userSlug}/{workspaceSlug}/audit-logs.csv
```

Accepts the same query parameters as the list endpoint and streams CSV with these columns:

```
created_at, event, subject_type, subject_id, description, user_id, actor_type, metadata
```

Useful for compliance review. Capped at 100,000 rows per export -- narrow the date range if you need more.

## Standard Event Names

| Event                    | Subject       | Fired when                                            |
|--------------------------|---------------|-------------------------------------------------------|
| `cert.received`          | Certificate   | SAP delivered a new certificate via `putCert`         |
| `cert.activated`         | Certificate   | A certificate was activated                           |
| `cert.deactivated`       | Certificate   | A certificate was deactivated                         |
| `cert.deleted`           | Certificate   | A certificate was removed                             |
| `repository.created`     | Repository    | A new source was created                              |
| `repository.updated`     | Repository    | A source's config changed                             |
| `repository.deleted`     | Repository    | A source was deleted                                  |
| `destination.created`    | Destination   | A new destination was created                         |
| `destination.updated`    | Destination   | A destination's config or credentials changed         |
| `destination.deleted`    | Destination   | A destination was deleted                             |
| `connection.created`     | Connection    | A new pipeline was created                            |
| `connection.toggled`     | Connection    | A pipeline was activated or deactivated               |
| `connection.deleted`     | Connection    | A pipeline was deleted                                |
| `document.retried`       | Document      | A document was manually retried                       |
| `apikey.created`         | ApiKey        | An API key was created                                |
| `apikey.revoked`         | ApiKey        | An API key was revoked                                |
| `rfc.call`               | RfcCall       | An RFC was invoked through the JCo bridge             |

## Retention

| Resource           | Default retention | Configurable                              |
|--------------------|-------------------|-------------------------------------------|
| Captured requests  | 30 days           | Yes, per workspace (max 90 days)          |
| Captured bodies    | 7 days            | Yes, per workspace (max 30 days)          |
| Audit log          | 365 days          | Compliance-tier workspaces: indefinite    |

Bodies are pruned on a separate schedule from metadata so the request history remains queryable after the payload itself has been swept.

## Redaction

The following keys are automatically redacted to `[REDACTED]` in stored headers and JSON bodies before persistence:

- `authorization`, `proxy-authorization`
- `cookie`, `set-cookie`
- Any header or JSON key matching `*token*`, `*secret*`, `*password*`, `*api-key*` (case-insensitive)

The original values are still used at request-handling time -- redaction is a write-side concern, not a runtime mutation.

## Next Steps

- [Documents API](/docs/api/documents) -- Correlate captured requests with delivered documents
- [Certificates API](/docs/api/certificates) -- Inspect certs referenced in `cert.*` audit events
- [RFC API](/docs/api/rfc) -- See JCo calls in the same request stream (`channel=jco`)
