# Topics

Source: https://ceyo.ai/docs/signal/topics

### Topics

Create and manage topic groups used to organize prompts for the selected project or location.

**Scope:** Project

### List topics

`GET /projects/{project_id}/visibility/topics`

Returns paginated topics in the selected project, ordered deterministically by created\_at, then id.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |

#### Query parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `page` | integer | Optional; Default: 1 | 1-based page number. |
| `per_page` | integer | Optional; Default: 25 | Topics per page. Maximum: 100. |

#### Response envelope

`project_id`:**uuid**`topics`:**Topic\[\]**`pagination`:**Pagination**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `topics` | Topic\[\] |  | Topics ordered by created\_at, then id. |
| `pagination` | Pagination |  | Topic pagination metadata. |

#### Topic

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique topic identifier. |
| `name` | string |  | Customer-facing topic name; maximum 200 characters and case-insensitively unique within this visibility scope. |
| `created_at` | datetime |  | Topic creation time in ISO 8601 format. |
| `updated_at` | datetime |  | Time the topic was last updated. |

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/visibility/topics?page=1&per_page=25' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "topics": [
    {
      "id": "ab20526b-6bb2-436c-8c93-5bf77ea43848",
      "name": "AI visibility platforms",
      "created_at": "2026-07-20T08:30:00Z",
      "updated_at": "2026-07-20T08:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 25,
    "total": 1,
    "total_pages": 1
  }
}
```

#### Pagination

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `page` | integer |  | Current 1-based page. |
| `per_page` | integer |  | Records requested per page. |
| `total` | integer |  | Total records matching the filters. |
| `total_pages` | integer |  | Total available pages. |

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [
      {
        "field": "name",
        "message": "must be present"
      }
    ],
    "request_id": "req_01K1GP6J8QQFZ4D2B6C5A9V3TS"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request \| invalid\_date\_range \| range\_too\_large |  | A prompt filter, model, identifier, or metrics date range is invalid, or the range exceeds three months. |
| `401` | invalid\_api\_key |  | Authorization is absent or invalid. |
| `403` | forbidden |  | The API key cannot manage this visibility scope. |
| `404` | not\_found |  | The project, location, or topic was not found. |
| `409` | conflict |  | The requested lifecycle operation conflicts with current state. |
| `422` | validation\_failed \| prompt\_limit\_reached \| daily\_prompt\_activation\_limit\_reached |  | Input is invalid, a suggested-prompt status is unsupported, capacity is exhausted, or a bulk request exceeds 100 rows. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Create topic

`POST /projects/{project_id}/visibility/topics`

Creates a topic in the selected project. Topic names should represent a durable customer question area rather than an individual query.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |

#### Request body

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `name` | string |  | Required topic name; maximum 200 characters and case-insensitively unique within the scope. |

#### Response envelope

`project_id`:**uuid**`topic`:**Topic**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `topic` | Topic |  | Created topic. |

#### Topic

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique topic identifier. |
| `name` | string |  | Customer-facing topic name; maximum 200 characters and case-insensitively unique within this visibility scope. |
| `created_at` | datetime |  | Topic creation time in ISO 8601 format. |
| `updated_at` | datetime |  | Time the topic was last updated. |

#### Request and response

```curl
curl --request POST \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/visibility/topics' \
  --header 'Authorization: Bearer ceyo_platform_...' \
  --header 'Content-Type: application/json' \
  --data '{"name":"AI visibility platforms"}'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "topic": {
    "id": "ab20526b-6bb2-436c-8c93-5bf77ea43848",
    "name": "AI visibility platforms",
    "created_at": "2026-07-20T08:30:00Z",
    "updated_at": "2026-07-20T08:30:00Z"
  }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [
      {
        "field": "name",
        "message": "must be present"
      }
    ],
    "request_id": "req_01K1GP6J8QQFZ4D2B6C5A9V3TS"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request \| invalid\_date\_range \| range\_too\_large |  | A prompt filter, model, identifier, or metrics date range is invalid, or the range exceeds three months. |
| `401` | invalid\_api\_key |  | Authorization is absent or invalid. |
| `403` | forbidden |  | The API key cannot manage this visibility scope. |
| `404` | not\_found |  | The project, location, or topic was not found. |
| `409` | conflict |  | The requested lifecycle operation conflicts with current state. |
| `422` | validation\_failed \| prompt\_limit\_reached \| daily\_prompt\_activation\_limit\_reached |  | Input is invalid, a suggested-prompt status is unsupported, capacity is exhausted, or a bulk request exceeds 100 rows. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Get topic

`GET /projects/{project_id}/visibility/topics/{topic_id}`

Returns one topic from the selected project. Use the prompt list with topic\_ids to retrieve its prompts and metrics.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `topic_id` | uuid | Required | Topic identifier. |

#### Response envelope

`project_id`:**uuid**`topic`:**Topic**

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `topic` | Topic |  | Requested topic. |

#### Topic

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique topic identifier. |
| `name` | string |  | Customer-facing topic name; maximum 200 characters and case-insensitively unique within this visibility scope. |
| `created_at` | datetime |  | Topic creation time in ISO 8601 format. |
| `updated_at` | datetime |  | Time the topic was last updated. |

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/visibility/topics/{topic_id}' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "topic": {
    "id": "ab20526b-6bb2-436c-8c93-5bf77ea43848",
    "name": "AI visibility platforms",
    "created_at": "2026-07-20T08:30:00Z",
    "updated_at": "2026-07-20T08:30:00Z"
  }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [
      {
        "field": "name",
        "message": "must be present"
      }
    ],
    "request_id": "req_01K1GP6J8QQFZ4D2B6C5A9V3TS"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request \| invalid\_date\_range \| range\_too\_large |  | A prompt filter, model, identifier, or metrics date range is invalid, or the range exceeds three months. |
| `401` | invalid\_api\_key |  | Authorization is absent or invalid. |
| `403` | forbidden |  | The API key cannot manage this visibility scope. |
| `404` | not\_found |  | The project, location, or topic was not found. |
| `409` | conflict |  | The requested lifecycle operation conflicts with current state. |
| `422` | validation\_failed \| prompt\_limit\_reached \| daily\_prompt\_activation\_limit\_reached |  | Input is invalid, a suggested-prompt status is unsupported, capacity is exhausted, or a bulk request exceeds 100 rows. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Delete topic

`DELETE /projects/{project_id}/visibility/topics/{topic_id}`

Starts irreversible asynchronous deletion of a topic and its dependent visibility data. This is not an archive operation.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `topic_id` | uuid | Required | Topic identifier. |

> **Irreversible**
>
> Topic deletion is asynchronous and permanent. Archive individual prompts when they may need to be restored later.

#### Request and response

```curl
curl --request DELETE \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/visibility/topics/{topic_id}' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
HTTP/1.1 202 Accepted
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid.",
    "details": [
      {
        "field": "name",
        "message": "must be present"
      }
    ],
    "request_id": "req_01K1GP6J8QQFZ4D2B6C5A9V3TS"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request \| invalid\_date\_range \| range\_too\_large |  | A prompt filter, model, identifier, or metrics date range is invalid, or the range exceeds three months. |
| `401` | invalid\_api\_key |  | Authorization is absent or invalid. |
| `403` | forbidden |  | The API key cannot manage this visibility scope. |
| `404` | not\_found |  | The project, location, or topic was not found. |
| `409` | conflict |  | The requested lifecycle operation conflicts with current state. |
| `422` | validation\_failed \| prompt\_limit\_reached \| daily\_prompt\_activation\_limit\_reached |  | Input is invalid, a suggested-prompt status is unsupported, capacity is exhausted, or a bulk request exceeds 100 rows. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |
