> For the complete documentation index, see [llms.txt](https://docs.cuoral.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.cuoral.com/api-reference/page-collection.md).

# Page Collection

This allows you to group related pages or routes and analyze their performance collectively.

The Page Collections API allows you to group related pages and analyze their combined performance.

Collections can contain pages using either:

* **Explicit page IDs** — map specific pages directly to a collection.
* **URL prefix patterns** — automatically include pages whose URLs match a specified prefix.

For example, a collection can contain all pages under `/ticketing`, or specific pages identified by their `page_id`.

### Base URL

All endpoints are available under:

```
/external/v1/page-collections
```

### Authentication

Requests must be authenticated using your API key.

Include the API key in the `X-API-Key` header:

```http
X-API-Key: YOUR_API_KEY
```

Example:

```bash
curl -X GET "https://api.cuoral.com/external/v1/page-collections" \
  -H "X-API-Key: YOUR_API_KEY"
```

> Replace `https://api.cuoral.com` with your API's base URL.

***

## Endpoints

| Method   | Endpoint                                                               | Description                  |
| -------- | ---------------------------------------------------------------------- | ---------------------------- |
| `GET`    | `/external/v1/page-collections`                                        | List all page collections    |
| `POST`   | `/external/v1/page-collections`                                        | Create a page collection     |
| `GET`    | `/external/v1/page-collections/pages`                                  | List registry pages          |
| `GET`    | `/external/v1/page-collections/{collection_id}`                        | Get collection analytics     |
| `PUT`    | `/external/v1/page-collections/{collection_id}`                        | Update a collection          |
| `DELETE` | `/external/v1/page-collections/{collection_id}`                        | Delete a collection          |
| `POST`   | `/external/v1/page-collections/{collection_id}/map-pages`              | Map pages to a collection    |
| `DELETE` | `/external/v1/page-collections/{collection_id}/pages/{page_id}`        | Remove a page mapping        |
| `DELETE` | `/external/v1/page-collections/{collection_id}/patterns/{url_pattern}` | Remove a URL pattern mapping |

***

## List Page Collections

Returns all page collections belonging to the authenticated organization.

#### Endpoint

```http
GET /external/v1/page-collections
```

#### Authentication

Requires `X-API-Key`.

#### Query Parameters

None.

#### Example Request

```bash
curl -X GET "https://api.cuoral.com/external/v1/page-collections" \
  -H "X-API-Key: YOUR_API_KEY"
```

#### Response

**200 OK**

```json
{
  "collections": [
    {
      "collection_id": "collection_123",
      "name": "Ticketing",
      "description": "All ticketing-related pages",
      "member_pages_count": 12,
      "created_at": "2026-08-19T21:58:51.218Z",
      "updated_at": "2026-08-19T21:58:51.218Z"
    }
  ]
}
```

#### Response Fields

| Field                | Type    | Description                                 |
| -------------------- | ------- | ------------------------------------------- |
| `collections`        | array   | List of collections                         |
| `collection_id`      | string  | Unique identifier for the collection        |
| `name`               | string  | Collection name                             |
| `description`        | string  | Collection description                      |
| `member_pages_count` | integer | Number of pages currently in the collection |
| `created_at`         | string  | Collection creation timestamp               |
| `updated_at`         | string  | Last update timestamp                       |

***

## Create Page Collection

Creates a new collection for grouping related pages.

Pages can be added using explicit `page_ids`, URL prefix rules, or both.

#### Endpoint

```http
POST /external/v1/page-collections
```

#### Authentication

Requires `X-API-Key`.

#### Request Body

```json
{
  "name": "Ticketing",
  "description": "All ticketing-related pages",
  "page_ids": [
    "page_123",
    "page_456"
  ],
  "url_patterns": [
    "/ticketing"
  ]
}
```

#### Request Fields

| Field          | Type             | Required | Description                            |
| -------------- | ---------------- | -------- | -------------------------------------- |
| `name`         | string           | Yes      | Name of the collection                 |
| `description`  | string           | No       | Description of the collection          |
| `page_ids`     | array of strings | No       | Explicit page IDs to include           |
| `url_patterns` | array of strings | No       | URL prefix rules used to include pages |

#### URL Patterns

URL patterns are treated as prefixes.

For example:

```
/ticketing
```

can match pages such as:

```
/ticketing
/ticketing/create
/ticketing/list
/ticketing/settings
```

#### Example Request

```bash
curl -X POST "https://api.cuoral.com/external/v1/page-collections" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ticketing",
    "description": "All ticketing-related pages",
    "page_ids": [
      "page_123",
      "page_456"
    ],
    "url_patterns": [
      "/ticketing"
    ]
  }'
```

#### Response

**200 OK**

```json
{
  "collection_id": "collection_123",
  "name": "Ticketing",
  "description": "All ticketing-related pages",
  "start": "2026-08-19",
  "end": "2026-08-19",
  "summary": {
    "total_visits": 0,
    "unique_users": 0,
    "avg_time_seconds": 0,
    "engagement_score": 0,
    "friction_score": 0,
    "error_count": 0,
    "rage_click_count": 0,
    "drop_off_rate": 0,
    "member_pages": 0
  },
  "trend": [],
  "members": [],
  "warnings": []
}
```

#### Validation Error

**422 Unprocessable Entity**

```json
{
  "detail": [
    {
      "loc": [
        "body",
        "name"
      ],
      "msg": "string",
      "type": "string",
      "input": "string"
    }
  ]
}
```

***

## List Registry Pages

Returns the organization's page registry.

Use this endpoint to retrieve available `page_id` values before mapping pages to a collection.

#### Endpoint

```http
GET /external/v1/page-collections/pages
```

#### Query Parameters

| Parameter | Type    | Required | Default | Description                                    |
| --------- | ------- | -------- | ------- | ---------------------------------------------- |
| `search`  | string  | No       | —       | Search by title, canonical URL, or display URL |
| `page`    | integer | No       | `1`     | Page number. Must be `>= 1`                    |
| `limit`   | integer | No       | `100`   | Maximum number of results. Range: `1–200`      |

#### Example Request

```bash
curl -X GET "https://api.cuoral.com/external/v1/page-collections/pages?search=ticketing&page=1&limit=100" \
  -H "X-API-Key: YOUR_API_KEY"
```

#### Response

**200 OK**

```json
{
  "pages": [
    {
      "page_id": "page_123",
      "title": "Create a Ticket",
      "canonical_url": "/ticketing/create",
      "display_url": "https://cuoral.com/ticketing/create"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 100,
    "total": 1
  },
  "search": "ticketing"
}
```

#### Response Fields

| Field                   | Type    | Description                      |
| ----------------------- | ------- | -------------------------------- |
| `pages`                 | array   | Matching registry pages          |
| `pages[].page_id`       | string  | Unique page identifier           |
| `pages[].title`         | string  | Page title                       |
| `pages[].canonical_url` | string  | Canonical page URL               |
| `pages[].display_url`   | string  | Display URL                      |
| `pagination.page`       | integer | Current page number              |
| `pagination.limit`      | integer | Number of results requested      |
| `pagination.total`      | integer | Total number of matching pages   |
| `search`                | string  | Search term used for the request |

#### Pagination Example

To retrieve the second page of results:

```http
GET /external/v1/page-collections/pages?page=2&limit=100
```

***

## Get Page Collection Analytics

Returns consolidated analytics across all pages belonging to a collection.

#### Endpoint

```http
GET /external/v1/page-collections/{collection_id}
```

#### Path Parameters

| Parameter       | Type   | Required | Description                  |
| --------------- | ------ | -------- | ---------------------------- |
| `collection_id` | string | Yes      | Unique collection identifier |

#### Query Parameters

| Parameter  | Type   | Required | Default | Description                       |
| ---------- | ------ | -------- | ------- | --------------------------------- |
| `start`    | date   | No       | —       | Start date in `YYYY-MM-DD` format |
| `end`      | date   | No       | —       | End date in `YYYY-MM-DD` format   |
| `interval` | string | No       | `daily` | Trend interval                    |

Supported intervals:

* `hourly`
* `daily`
* `weekly`
* `monthly`

#### Example Request

```bash
curl -X GET "https://api.cuoral.com/external/v1/page-collections/collection_123?start=2026-08-01&end=2026-08-19&interval=daily" \
  -H "X-API-Key: YOUR_API_KEY"
```

#### Response

**200 OK**

```json
{
  "collection_id": "collection_123",
  "name": "Ticketing",
  "description": "All ticketing-related pages",
  "start": "2026-08-01",
  "end": "2026-08-19",
  "summary": {
    "total_visits": 12500,
    "unique_users": 8400,
    "avg_time_seconds": 184,
    "engagement_score": 82,
    "friction_score": 18,
    "error_count": 42,
    "rage_click_count": 17,
    "drop_off_rate": 0.12,
    "member_pages": 12
  },
  "trend": [],
  "members": [],
  "warnings": []
}
```

#### Analytics Fields

| Field              | Type   | Description                                 |
| ------------------ | ------ | ------------------------------------------- |
| `total_visits`     | number | Total visits across collection pages        |
| `unique_users`     | number | Number of unique users                      |
| `avg_time_seconds` | number | Average time spent on pages, in seconds     |
| `engagement_score` | number | Collection engagement score                 |
| `friction_score`   | number | Collection friction score                   |
| `error_count`      | number | Number of recorded errors                   |
| `rage_click_count` | number | Number of rage-click events                 |
| `drop_off_rate`    | number | Drop-off rate                               |
| `member_pages`     | number | Number of pages belonging to the collection |

#### Trend Intervals

Use `interval` to control the granularity of trend data:

```
hourly
daily
weekly
monthly
```

For example:

```http
GET /external/v1/page-collections/collection_123?interval=weekly
```

***

## Update Page Collection

Updates the name and/or description of an existing collection.

#### Endpoint

```http
PUT /external/v1/page-collections/{collection_id}
```

#### Path Parameters

| Parameter       | Type   | Required | Description                  |
| --------------- | ------ | -------- | ---------------------------- |
| `collection_id` | string | Yes      | Unique collection identifier |

#### Request Body

```json
{
  "name": "Customer Support",
  "description": "Pages related to customer support workflows"
}
```

#### Request Fields

| Field         | Type   | Required | Description                    |
| ------------- | ------ | -------- | ------------------------------ |
| `name`        | string | No       | Updated collection name        |
| `description` | string | No       | Updated collection description |

#### Example Request

```bash
curl -X PUT "https://api.cuoral.com/external/v1/page-collections/collection_123" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Customer Support",
    "description": "Pages related to customer support workflows"
  }'
```

#### Response

**200 OK**

```json
{
  "collection_id": "collection_123",
  "name": "Customer Support",
  "description": "Pages related to customer support workflows",
  "start": "2026-08-19",
  "end": "2026-08-19",
  "summary": {
    "total_visits": 0,
    "unique_users": 0,
    "avg_time_seconds": 0,
    "engagement_score": 0,
    "friction_score": 0,
    "error_count": 0,
    "rage_click_count": 0,
    "drop_off_rate": 0,
    "member_pages": 0
  },
  "trend": [],
  "members": [],
  "warnings": []
}
```

***

## Delete Page Collection

Deletes a collection and its mappings.

> **Important:** Deleting a collection does not delete the underlying pages or their analytics data.

#### Endpoint

```http
DELETE /external/v1/page-collections/{collection_id}
```

#### Path Parameters

| Parameter       | Type   | Required | Description                  |
| --------------- | ------ | -------- | ---------------------------- |
| `collection_id` | string | Yes      | Unique collection identifier |

#### Example Request

```bash
curl -X DELETE "https://api.cuoral.com/external/v1/page-collections/collection_123" \
  -H "X-API-Key: YOUR_API_KEY"
```

#### Response

**200 OK**

```json
"Page collection deleted successfully"
```

***

## Map Pages to Collection

Adds pages and/or URL prefix rules to an existing collection.

You can use this endpoint to add mappings after a collection has already been created.

#### Endpoint

```http
POST /external/v1/page-collections/{collection_id}/map-pages
```

#### Path Parameters

| Parameter       | Type   | Required | Description                  |
| --------------- | ------ | -------- | ---------------------------- |
| `collection_id` | string | Yes      | Unique collection identifier |

#### Request Body

```json
{
  "page_ids": [
    "page_123",
    "page_456"
  ],
  "url_patterns": [
    "/ticketing"
  ]
}
```

#### Request Fields

| Field          | Type             | Required | Description                |
| -------------- | ---------------- | -------- | -------------------------- |
| `page_ids`     | array of strings | No       | Page IDs to explicitly add |
| `url_patterns` | array of strings | No       | URL prefix rules to add    |

Both fields can be used together.

#### Example Request

```bash
curl -X POST "https://api.cuoral.com/external/v1/page-collections/collection_123/map-pages" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "page_ids": [
      "page_123",
      "page_456"
    ],
    "url_patterns": [
      "/ticketing"
    ]
  }'
```

#### Response

**200 OK**

```json
{
  "collection_id": "collection_123",
  "name": "Ticketing",
  "description": "All ticketing-related pages",
  "start": "2026-08-19",
  "end": "2026-08-19",
  "summary": {
    "total_visits": 0,
    "unique_users": 0,
    "avg_time_seconds": 0,
    "engagement_score": 0,
    "friction_score": 0,
    "error_count": 0,
    "rage_click_count": 0,
    "drop_off_rate": 0,
    "member_pages": 0
  },
  "trend": [],
  "members": [],
  "warnings": []
}
```

***

## Unmap Page from Collection

Removes an explicit page mapping from a collection.

The page itself is not deleted, and its analytics data remains unchanged.

#### Endpoint

```http
DELETE /external/v1/page-collections/{collection_id}/pages/{page_id}
```

#### Path Parameters

| Parameter       | Type   | Required | Description                        |
| --------------- | ------ | -------- | ---------------------------------- |
| `collection_id` | string | Yes      | Unique collection identifier       |
| `page_id`       | string | Yes      | Page to remove from the collection |

#### Example Request

```bash
curl -X DELETE "https://api.cuoral.com/external/v1/page-collections/collection_123/pages/page_456" \
  -H "X-API-Key: YOUR_API_KEY"
```

#### Response

**200 OK**

```json
{
  "collection_id": "collection_123",
  "name": "Ticketing",
  "description": "All ticketing-related pages",
  "start": "2026-08-19",
  "end": "2026-08-19",
  "summary": {
    "total_visits": 0,
    "unique_users": 0,
    "avg_time_seconds": 0,
    "engagement_score": 0,
    "friction_score": 0,
    "error_count": 0,
    "rage_click_count": 0,
    "drop_off_rate": 0,
    "member_pages": 0
  },
  "trend": [],
  "members": [],
  "warnings": []
}
```

> **Note:** This only removes the explicit page mapping. If the page also matches a URL prefix pattern, it may remain a member of the collection through that pattern.

***

## Unmap URL Pattern from Collection

Removes a URL prefix rule from a collection.

Removing a pattern causes pages matched exclusively by that pattern to leave the collection. The underlying pages and their analytics data remain untouched.

#### Endpoint

```http
DELETE /external/v1/page-collections/{collection_id}/patterns/{url_pattern}
```

#### Path Parameters

| Parameter       | Type   | Required | Description                  |
| --------------- | ------ | -------- | ---------------------------- |
| `collection_id` | string | Yes      | Unique collection identifier |
| `url_pattern`   | string | Yes      | URL prefix pattern to remove |

#### URL Pattern Format

A leading `/` is optional.

Both of the following are valid:

```
/ticketing
```

```
ticketing
```

Patterns can also contain nested paths:

```
ticketing/list
```

#### Example Request

```bash
curl -X DELETE "https://api.cuoral.com/external/v1/page-collections/collection_123/patterns/ticketing" \
  -H "X-API-Key: YOUR_API_KEY"
```

#### Response

**200 OK**

```json
{
  "collection_id": "collection_123",
  "name": "Ticketing",
  "description": "All ticketing-related pages",
  "start": "2026-08-19",
  "end": "2026-08-19",
  "summary": {
    "total_visits": 0,
    "unique_users": 0,
    "avg_time_seconds": 0,
    "engagement_score": 0,
    "friction_score": 0,
    "error_count": 0,
    "rage_click_count": 0,
    "drop_off_rate": 0,
    "member_pages": 0,
    "warnings": []
  },
  "trend": [],
  "members": [],
  "warnings": []
}
```

***

## Common Error Response

Endpoints that accept path, query, or body parameters may return a `422 Unprocessable Entity` response when validation fails.

#### 422 Unprocessable Entity

```json
{
  "detail": [
    {
      "loc": [
        "body",
        "name"
      ],
      "msg": "Field required",
      "type": "missing",
      "input": {}
    }
  ]
}
```

#### Error Fields

| Field    | Description                       |
| -------- | --------------------------------- |
| `detail` | List of validation errors         |
| `loc`    | Location of the invalid parameter |
| `msg`    | Human-readable validation message |
| `type`   | Validation error type             |
| `input`  | Value supplied in the request     |

***

## Recommended Workflow

A typical workflow for creating and managing a page collection is:

#### 1. Retrieve available pages

Use:

```http
GET /external/v1/page-collections/pages
```

Search for the pages you want to include and collect their `page_id` values.

#### 2. Create the collection

Use:

```http
POST /external/v1/page-collections
```

Provide the collection name and optionally include `page_ids` and/or `url_patterns`.

#### 3. Add additional mappings

If needed, use:

```http
POST /external/v1/page-collections/{collection_id}/map-pages
```

#### 4. View collection analytics

Use:

```http
GET /external/v1/page-collections/{collection_id}
```

Specify `start`, `end`, and `interval` to control the analytics period and trend granularity.

#### 5. Remove mappings

Remove individual pages with:

```http
DELETE /external/v1/page-collections/{collection_id}/pages/{page_id}
```

Or remove URL prefix rules with:

```http
DELETE /external/v1/page-collections/{collection_id}/patterns/{url_pattern}
```

#### 6. Update or delete the collection

Update collection metadata with:

```http
PUT /external/v1/page-collections/{collection_id}
```

Delete the collection with:

```http
DELETE /external/v1/page-collections/{collection_id}
```

Deleting a collection does **not** delete the underlying pages or analytics data.

***

## Page Mapping Behavior

Page Collections support two types of mappings:

### Explicit Page Mapping

An explicit mapping references a specific `page_id`.

Example:

```json
{
  "page_ids": [
    "page_123",
    "page_456"
  ]
}
```

This is useful when you want precise control over which pages belong to a collection.

### URL Prefix Mapping

A URL pattern includes pages whose URLs begin with the specified prefix.

Example:

```json
{
  "url_patterns": [
    "/ticketing"
  ]
}
```

This can include:

```
/ticketing
/ticketing/create
/ticketing/list
/ticketing/settings
```

URL patterns are useful when a collection should automatically cover an entire section of your application.

### Combining Both

You can use explicit page IDs and URL patterns together:

```json
{
  "page_ids": [
    "page_123"
  ],
  "url_patterns": [
    "/ticketing"
  ]
}
```

This allows a collection to contain both specifically selected pages and pages discovered through URL rules.
