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

# Search Clickwrap Events

> Search the recorded events for a clickwrap across all of its versions, filtered by end user, event type, date, or metadata

## Endpoint

```
POST /v2/server/clickwrap/{slug}/events/search
```

## Authentication

This endpoint requires an OAuth access token. Send it as a bearer token:

```bash theme={null}
Authorization: Bearer your_access_token
```

**Required scope:** `clickwrap:read`

> [!NOTE]
>
> Clickwrap is the one `/v2` surface with no acting user. The subject of a clickwrap event is
> `user_identifier`, an opaque string you supply for your own end user, which is never resolved
> against Chamelio's users. These endpoints are authorized by organization and scope alone, so
> `client_credentials` tokens work here without restriction.

## Path Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `slug` | string | Yes | Unique identifier for the clickwrap |

## Request Body

Every field is optional. An empty body (`{}`) returns the newest 50 events for the clickwrap.

| Field | Type | Required | Description |
| - | - | - | - |
| `user_identifier` | string or null | No | Case-insensitive partial match against `user_identifier`. `%` matches any sequence of characters; `_` matches one character. Check returned values when you need a literal match |
| `event_types` | array of strings or null | No | Return only these event types: `accepted`, `viewed`, `checked`, `scrolled_to_bottom` |
| `version_ids` | array of integers or null | No | Return only events recorded against these versions. Use the `clickwrap_version_id` values from [Get Clickwrap History](https://docs.chamelio.ai/api-reference/endpoint/v2/clickwrap/history.md) |
| `metadata_key` | string or null | No | Top-level key in the event's `metadata`. Must be sent together with `metadata_value` |
| `metadata_value` | string or null | No | Case-insensitive partial match against the value at `metadata_key`, with `%` and `_` wildcards as above. Check returned values when you need a literal match. Must be sent together with `metadata_key` |
| `from_date` | string or null | No | ISO 8601 timestamp. Return events recorded at or after this time |
| `to_date` | string or null | No | ISO 8601 timestamp. Return events recorded at or before this time. Cannot be earlier than `from_date` |
| `sort_order` | string | No | Sort by event time: `desc` (newest first, default) or `asc` |
| `offset` | integer | No | Number of events to skip. Minimum `0`, default `0` |
| `page_size` | integer | No | Number of events to return. Between `1` and `200`, default `50` |

## Request Example

```bash cURL theme={null}
curl -X POST "https://platform.chamelio.ai/v2/server/clickwrap/privacy-policy/events/search" \
  -H "Authorization: Bearer your_access_token" \
  -H "Content-Type: application/json" \
  -d '{
    "user_identifier": "user@example.com",
    "event_types": ["accepted"],
    "page_size": 20
  }'
```

```python Python theme={null}
import requests

slug = "privacy-policy"

url = f"https://platform.chamelio.ai/v2/server/clickwrap/{slug}/events/search"
headers = {
    "Authorization": "Bearer your_access_token",
    "Content-Type": "application/json"
}
body = {
    "user_identifier": "user@example.com",
    "event_types": ["accepted"],
    "page_size": 20
}

response = requests.post(url, headers=headers, json=body)
print(response.json())
```

```javascript JavaScript theme={null}
const slug = "privacy-policy";

const response = await fetch(
  `https://platform.chamelio.ai/v2/server/clickwrap/${slug}/events/search`,
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer your_access_token',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      user_identifier: 'user@example.com',
      event_types: ['accepted'],
      page_size: 20
    })
  }
);

const data = await response.json();
console.log(data);
```

## Response

### Success Response

**Status Code:** `200 OK`

```json theme={null}
{
  "events": [
    {
      "clickwrap_event_id": 1834,
      "clickwrap_id": 7,
      "clickwrap_title": "Privacy Policy",
      "clickwrap_slug": "privacy-policy",
      "event_type": "accepted",
      "user_identifier": "user@example.com",
      "ip_address": "203.0.113.42",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
      "metadata": {
        "session_id": "sess_abc123",
        "product": "checkout",
        "certificate_attachment_id": 5521,
        "template_variables": {"company_name": "Acme Inc"}
      },
      "version_number": 3,
      "created_at": "2026-10-20T14:32:08Z",
      "sor_document_processing_id": 90412,
      "sor_exported_at": "2026-10-20T14:32:41Z",
      "sor_export_error": null
    },
    {
      "clickwrap_event_id": 1102,
      "clickwrap_id": 7,
      "clickwrap_title": "Privacy Policy",
      "clickwrap_slug": "privacy-policy",
      "event_type": "accepted",
      "user_identifier": "user@example.com",
      "ip_address": "203.0.113.42",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
      "metadata": {
        "session_id": "sess_9f8e7d",
        "product": "checkout"
      },
      "version_number": 2,
      "created_at": "2026-03-04T10:15:22Z",
      "sor_document_processing_id": null,
      "sor_exported_at": null,
      "sor_export_error": null
    }
  ],
  "total": 2
}
```

### Response Fields

| Field | Type | Description |
| - | - | - |
| `events` | array | The matching events for this page, sorted by `created_at` in `sort_order` |
| `total` | integer | Total matching events across all pages, except that an empty or out-of-range page returns `0` even if earlier pages contain matches |

### Event Object Fields

| Field | Type | Description |
| - | - | - |
| `clickwrap_event_id` | integer | Unique identifier of the event |
| `clickwrap_id` | integer | Internal identifier of the clickwrap |
| `clickwrap_title` | string | Title of the clickwrap |
| `clickwrap_slug` | string | Slug of the clickwrap |
| `event_type` | string | `accepted`, `viewed`, `checked`, or `scrolled_to_bottom` |
| `user_identifier` | string | The end-user identifier sent when the event was captured |
| `ip_address` | string or null | End user's IP address, if it was sent at capture |
| `user_agent` | string or null | End user's user agent, if it was sent at capture |
| `metadata` | object or null | Stored custom metadata plus `template_variables` when a nonempty `variables` object was sent at capture. On `accepted` events Chamelio adds `certificate_attachment_id` once the acceptance PDF has been generated |
| `version_number` | integer or null | Sequential number of the version the event was recorded against |
| `created_at` | string | ISO 8601 timestamp when the event was recorded |
| `sor_document_processing_id` | integer or null | Knowledge Center processing ID of the exported acceptance PDF. Null until exported, and always null when export to the Knowledge Center is off for the clickwrap |
| `sor_exported_at` | string or null | ISO 8601 timestamp when the acceptance PDF was exported to the Knowledge Center |
| `sor_export_error` | string or null | Error message if storing or exporting the acceptance PDF failed |

## Error Responses

### 401 Unauthorized

Returned when the access token is missing, unknown, revoked, or expired, or when an `X-API-Key` was
sent instead of a bearer token. See [OAuth error responses](https://docs.chamelio.ai/api-reference/oauth-apps.md#error-responses).

```json theme={null}
{
  "detail": "Invalid access token"
}
```

### 403 Forbidden

Returned when the token does not carry the required scope.

```json theme={null}
{
  "detail": "Insufficient scope; this endpoint requires: clickwrap:read"
}
```

### 404 Not Found

Returned when no clickwrap exists for the given slug.

```json theme={null}
{
  "detail": "Not found"
}
```

### 422 Unprocessable Entity

Returned when the request body fails validation: for example, only one of `metadata_key` and
`metadata_value` is sent, `from_date` is later than `to_date`, `page_size` is outside 1-200, or an
`event_types` entry is not a known event type.

```json theme={null}
{
  "detail": [
    {
      "loc": ["body"],
      "msg": "Value error, metadata_key and metadata_value must be provided together",
      "type": "value_error"
    }
  ]
}
```

### 429 Too Many Requests

Returned when your organization exceeds its per-minute request limit.

```json theme={null}
{
  "detail": "Rate limit exceeded"
}
```

### 500 Internal Server Error

Returned when the request fails due to a server error.

```json theme={null}
{
  "detail": "Failed to search clickwrap events"
}
```

## Notes

> [!NOTE]
>
> The search covers every version of the clickwrap, including archived ones, so it returns a user's
> full history. Unlike the other clickwrap endpoints, it also works for a clickwrap that has no
> published version yet.

> [!WARNING]
>
> `user_identifier` and `metadata_value` are partial matches: `user@example.com` also matches
> `other-user@example.com`. `%` matches any sequence of characters and `_` matches one character;
> for example, `user_1` also matches `userA1`. When you need a literal user or metadata value,
> check that value on each returned event.

> [!NOTE]
>
> For personalized agreements, `metadata.template_variables` contains the values supplied through
> the [v2 capture request](https://docs.chamelio.ai/api-reference/endpoint/v2/clickwrap/capture.md). The acceptance PDF uses
> those values with the recorded version's template. PDF generation is asynchronous, so
> `certificate_attachment_id` may be absent when you first retrieve the event.

> [!TIP]
>
> To page through results, increase `offset` by `page_size` until you have read `total` events. A page
> past the last result comes back with an empty `events` array and `total` set to `0`.

> [!NOTE]
>
> The result is organization-global. Every token issued for the same organization gets the same
> answer - the token's user is used only to authenticate and scope-check the request.

## Use Cases

This endpoint is useful for:

* **Compliance evidence** - Pull every acceptance a user has recorded, with the version, IP address, user agent, and metadata of each
* **Reconciliation** - Find events by your own metadata (e.g. an order or session ID) to match them to records in your system
* **Funnel reporting** - Count `viewed` versus `accepted` events over a date range
