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

# GET /logs — query and filter captured webhook events

> GET /logs — query captured webhook events with rich filter syntax, offset or cursor pagination, and sparse field selection. Plus /logs/:logId endpoints.

`GET /logs` queries the DuckDB read model for captured webhook events. You can filter by almost any field, paginate with offsets or cursors, sort by multiple columns, and request sparse field sets. Two sub-endpoints let you fetch a single event by ID or retrieve its raw payload.

**Authentication:** Required when `authKey` is configured.

## Query parameters

<ParamField query="id" type="string">
  Exact log ID filter.
</ParamField>

<ParamField query="webhookId" type="string">
  Exact webhook ID filter.
</ParamField>

<ParamField query="requestUrl" type="string">
  Partial match against the stored request URL.
</ParamField>

<ParamField query="method" type="string">
  Exact HTTP method, normalized to uppercase (e.g. `POST`, `GET`).
</ParamField>

<ParamField query="statusCode" type="number">
  Exact status code or range syntax. Example: `statusCode[gte]=400`.
</ParamField>

<ParamField query="contentType" type="string">
  Partial match against the stored content type.
</ParamField>

<ParamField query="requestId" type="string">
  Exact request ID filter.
</ParamField>

<ParamField query="remoteIp" type="string">
  Exact IP address or CIDR block.
</ParamField>

<ParamField query="userAgent" type="string">
  Partial match against the user agent string.
</ParamField>

<ParamField query="signatureValid" type="boolean">
  Filter by signature verification result (`true` or `false`).
</ParamField>

<ParamField query="signatureProvider" type="string">
  Exact signature provider name (e.g. `stripe`, `github`).
</ParamField>

<ParamField query="processingTime" type="number">
  Exact or ranged server-side processing time in milliseconds. Excludes any configured `responseDelayMs`. Example: `processingTime[lt]=1000`.
</ParamField>

<ParamField query="size" type="number">
  Exact or ranged payload size in bytes. Example: `size[gte]=1024`.
</ParamField>

<ParamField query="timestamp" type="string">
  Exact or ranged ISO 8601 timestamp. Example: `timestamp[lte]=2026-01-30T12:00:00.000Z`.
</ParamField>

<ParamField query="startTime" type="string">
  Convenience lower bound for timestamp filtering. Equivalent to `timestamp[gte]`.
</ParamField>

<ParamField query="endTime" type="string">
  Convenience upper bound for timestamp filtering. Equivalent to `timestamp[lte]`.
</ParamField>

<ParamField query="headers" type="string">
  Substring search over serialized headers, or keyed JSON filtering. Example: `headers[x-request-id]=abc`.
</ParamField>

<ParamField query="query" type="string">
  Substring search over query string JSON, or keyed filtering. Example: `query[page]=2`.
</ParamField>

<ParamField query="body" type="string">
  Substring search over body JSON, or keyed filtering. Example: `body[event]=payment.success`. Nested paths use dot notation: `body[data.id]=evt_123`.
</ParamField>

<ParamField query="responseHeaders" type="string">
  Substring search over response header JSON, or keyed filtering.
</ParamField>

<ParamField query="responseBody" type="string">
  Substring search over response body JSON, or keyed filtering.
</ParamField>

<ParamField query="limit" type="number" default="10000">
  Maximum number of results to return. Invalid values are clamped to a minimum of `1`.
</ParamField>

<ParamField query="offset" type="number" default="0">
  Zero-based offset for traditional pagination.
</ParamField>

<ParamField query="cursor" type="string">
  Cursor for keyset pagination. When present, takes precedence over `offset`. Use the `nextCursor` value from a previous response.
</ParamField>

<ParamField query="sort" type="string" default="timestamp:DESC">
  Comma-separated sort rules. Example: `timestamp:desc,method:asc`.
</ParamField>

## Filter syntax

**Range filters** use bracket notation:

```text theme={null}
statusCode[gte]=400
processingTime[lt]=1000
timestamp[lte]=2026-01-30T12:00:00.000Z
```

Supported operators: `gte`, `gt`, `lte`, `lt`.

**Keyed JSON filters** match a specific field inside a stored JSON column:

```text theme={null}
body[event]=payment.success
headers[x-request-id]=abc
body[data.id]=evt_123
```

Use dot notation for nested paths. Without bracket notation, the filter performs a free-text substring search over the entire serialized column.

## Supported sort fields

`id`, `statusCode`, `method`, `size`, `timestamp`, `remoteIp`, `processingTime`, `webhookId`, `userAgent`, `requestUrl`, `contentType`, `requestId`, `signatureValid`, `signatureProvider`, `signatureError`

## Offset pagination response

```json theme={null}
{
  "filters": {
    "limit": 100,
    "offset": 0,
    "sort": [{ "field": "timestamp", "dir": "DESC" }],
    "webhookId": "wh_abc123"
  },
  "count": 1,
  "total": 150,
  "items": [
    {
      "id": "evt_8m2L5p9xR",
      "webhookId": "wh_abc123",
      "timestamp": "2026-01-30T12:00:00.000Z",
      "method": "POST",
      "statusCode": 200,
      "size": 1240,
      "processingTime": 12,
      "requestId": "req_abc123",
      "requestUrl": "/webhook/wh_abc123",
      "contentType": "application/json",
      "signatureValid": true,
      "signatureProvider": "stripe",
      "detailUrl": "https://<run-id>.runs.apify.net/logs/evt_8m2L5p9xR"
    }
  ],
  "nextOffset": 100,
  "nextPageUrl": "https://<run-id>.runs.apify.net/logs?webhookId=wh_abc123&limit=100&offset=100"
}
```

## Cursor pagination

When you pass a `cursor` value, the response returns `nextCursor` and `nextPageUrl` instead of `total` and `nextOffset`. Cursor pagination is more efficient for large datasets because it avoids a full `COUNT(*)` query.

```json theme={null}
{
  "count": 100,
  "items": [...],
  "nextCursor": "eyJpZCI6ImV2dF9YWFgifQ==",
  "nextPageUrl": "https://<run-id>.runs.apify.net/logs?cursor=eyJpZCI6ImV2dF9YWFgifQ=="
}
```

***

## GET /logs/:logId

Returns a single log entry by ID.

**Authentication:** Required when `authKey` is configured.

### Query parameters

<ParamField query="fields" type="string">
  Optional comma-separated list of fields to include in the response for a sparse result. If you request sparse fields, the handler still fetches `webhookId` internally for security validation and strips it from the response if you did not explicitly request it.
</ParamField>

### Response example

```json theme={null}
{
  "id": "evt_8m2L5p9xR",
  "webhookId": "wh_abc123",
  "timestamp": "2026-01-30T12:00:00.000Z",
  "method": "POST",
  "statusCode": 200,
  "headers": {
    "content-type": "application/json"
  },
  "query": {},
  "body": {
    "event": "payment.success"
  },
  "responseHeaders": {},
  "responseBody": "OK",
  "signatureValid": true,
  "signatureProvider": "stripe"
}
```

<Note>
  If the webhook associated with this log has expired or is no longer valid, the route returns `404` even when the log row still exists in DuckDB.
</Note>

***

## GET /logs/:logId/payload

Returns the original payload for a log entry. If the payload was offloaded to Apify KVS due to its size, this route hydrates it on demand.

**Authentication:** Required when `authKey` is configured.

The response `Content-Type` mirrors the original captured content type when available. The route returns:

* JSON when the stored payload is an object
* Raw text when the stored payload is scalar text
* Raw binary when the hydrated KVS value is a `Buffer`
