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

# Preview what deleting a keyword would destroy

> Lists the feeds that would be deleted along with this keyword (their filters reference only it) and the notifications attached to those feeds, which go with them. Changes nothing. Both arrays are empty when the keyword can be deleted with no collateral. A preview is a snapshot, not a lock — a feed created after it is answered is still destroyed by the delete; `DELETE /api/v2/keywords/{id}` reports what was ACTUALLY removed. 404 `KEYWORDS_NOT_FOUND` for an unknown id, exactly as the delete does.



## OpenAPI

````yaml https://app.octolens.com/api/v2/openapi.json get /api/v2/keywords/{id}/deletion-preview
openapi: 3.1.0
info:
  title: Octolens API
  version: 2.0.0
  description: >-
    The Octolens API lets you query mentions, manage keywords, and configure
    feeds programmatically. Every action available in the Octolens UI is
    available here.


    ### Authentication


    All v2 endpoints require an API key. Create one in **Settings > API** and
    pass it as `Authorization: Bearer <key>`. Keys are scoped to the
    organization they were minted in; you cannot access another org's data.


    API keys carry a scope — `read`, `write` (implies read), or `admin` (implies
    write). Each endpoint documents the scope it needs via the
    `x-required-scope` OpenAPI extension and the scope badge in the docs
    surface.


    ### Rate limiting


    The v2 API is rate-limited at **500 requests per hour per organization**,
    across all keys for that org. The limit resets at the top of each clock hour
    (fixed hourly window — the counter is keyed by the UTC hour bucket, not a
    rolling trailing hour).


    Every 2xx response carries three headers so clients can pace themselves:

    * `X-RateLimit-Limit` — the hourly cap (500)

    * `X-RateLimit-Remaining` — requests left in the current window

    * `X-RateLimit-Reset` — Unix timestamp (seconds) when the window resets


    When the cap is hit, the endpoint returns **429 Rate Limited** with an
    additional `Retry-After` header (seconds until the next window). The
    response body is the standard `ErrorResponse` with `code: "RATE_LIMITED"`.


    ### Response timing


    Every response also carries a standard `Server-Timing` header with the
    server-side breakdown of that request — `auth` (credential verification,
    plan gate and rate limiting), `handler` (the endpoint's own work) and
    `total`, in milliseconds. Use it to tell whether a slow call was spent in
    the API's request pipeline, in the query behind the endpoint, or on the
    network in between; browsers surface it automatically in devtools. Responses
    rejected before the endpoint ran carry `auth` and `total` only, and an
    endpoint may publish extra metrics of its own alongside these three — parse
    the header as a list, not as a fixed triple.


    ### Error handling


    All non-2xx responses share the same `ErrorResponse` envelope: `{ error: {
    code, message, status, details? } }`. The `code` field is a stable
    `ApiErrorCode` enum — branch on it programmatically instead of parsing
    `message`. See the `ApiErrorCode` schema for the full catalog grouped by
    category.


    `VALIDATION_ERROR` (400) responses include a `details` array with per-field
    Zod issues — inspect `details[i].path` to pinpoint which input was rejected.


    ### Building filter bodies


    Endpoints that accept `simpleFilters` / `advancedFilters` (e.g. `POST
    /api/v2/mentions`, `PATCH /api/v2/feeds/{id}`) take a structured object that
    can be tricky to hand-craft. If you just have a natural-language description
    of what you want ("negative posts about pricing on reddit in the last
    week"), call `POST /api/v2/ai/filter-wizard` with that prompt and it will
    return a ready-to-use filter object you can pass straight through.
servers:
  - url: https://app.octolens.com
    description: Production
  - url: http://localhost:3000
    description: Local development
security:
  - ApiKey: []
paths:
  /api/v2/keywords/{id}/deletion-preview:
    get:
      tags:
        - Keywords
      summary: Preview what deleting a keyword would destroy
      description: >-
        Lists the feeds that would be deleted along with this keyword (their
        filters reference only it) and the notifications attached to those
        feeds, which go with them. Changes nothing. Both arrays are empty when
        the keyword can be deleted with no collateral. A preview is a snapshot,
        not a lock — a feed created after it is answered is still destroyed by
        the delete; `DELETE /api/v2/keywords/{id}` reports what was ACTUALLY
        removed. 404 `KEYWORDS_NOT_FOUND` for an unknown id, exactly as the
        delete does.
      operationId: previewKeywordDeletion
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            minimum: -9007199254740991
            maximum: 9007199254740991
          required: true
      responses:
        '200':
          description: 200 response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KeywordDeletionPreview'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden (insufficient plan or permissions)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    KeywordDeletionPreview:
      description: >-
        What deleting this keyword would destroy besides the keyword itself. A
        read — it changes nothing.
      type: object
      properties:
        keywordId:
          description: The keyword the preview is for.
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        keyword:
          description: The keyword's term.
          type: string
        feeds:
          description: >-
            Feeds that WOULD be deleted with this keyword. Empty when none
            would.
          type: array
          items:
            $ref: '#/components/schemas/CascadeDeletedFeed'
        notifications:
          description: >-
            Notifications that WOULD be deleted with those feeds. Empty when
            none would.
          type: array
          items:
            $ref: '#/components/schemas/CascadeDeletedNotification'
      required:
        - keywordId
        - keyword
        - feeds
        - notifications
      additionalProperties: false
    ErrorResponse:
      description: >-
        Standard error envelope returned for all non-2xx responses. The `code`
        field is stable — safe to branch on programmatically. `code`, `message`
        and `status` are always present; anything else is additive and per-code
        (`details` on `VALIDATION_ERROR`, `upgradeUrl`/`upgradeCommand` on
        `UPGRADE_REQUIRED`).
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              $ref: '#/components/schemas/ApiErrorCode'
              description: >-
                Machine-readable error code. See `ApiErrorCode` for the full
                list.
              example: NOT_FOUND
            message:
              description: Human-readable error message.
              example: Resource not found
              type: string
            status:
              description: HTTP status code — always matches the response status.
              example: 404
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            details:
              description: >-
                Present on `VALIDATION_ERROR` responses. Contains Zod issues
                describing each failing field (path, code, message).
              type: array
              items: {}
            upgradeUrl:
              description: >-
                Present on `UPGRADE_REQUIRED` responses (additive — see
                `ApiErrorCode`): the URL of the upgrade page for this workspace.
              example: https://app.octolens.com/me/upgrade?src=agents
              type: string
            upgradeCommand:
              description: >-
                Present on `UPGRADE_REQUIRED` responses (additive): the CLI
                command that mints a single-use, already-authenticated link into
                the upgrade page.
              example: octolens upgrade
              type: string
          required:
            - code
            - message
            - status
          additionalProperties: false
      required:
        - error
      additionalProperties: false
    CascadeDeletedFeed:
      description: A feed removed (or removable) as collateral of a keyword deletion.
      type: object
      properties:
        id:
          description: The feed's id.
          example: 25248
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        name:
          description: The feed's name.
          example: Acme mentions
          type: string
      required:
        - id
        - name
      additionalProperties: false
    CascadeDeletedNotification:
      description: >-
        A notification removed (or removable) as collateral of a keyword
        deletion — it goes when the feed it is attached to goes.
      type: object
      properties:
        id:
          description: The notification's id.
          example: 19
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        name:
          description: The notification's name.
          example: Acme alert
          type: string
        feedId:
          description: The feed this notification was attached to.
          example: 25248
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        destinationCount:
          description: How many delivery destinations the notification had.
          example: 1
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
      required:
        - id
        - name
        - feedId
        - destinationCount
      additionalProperties: false
    ApiErrorCode:
      description: >-
        Stable, machine-readable error code. Grouped as follows:


        **Auth / request shape** — `UNAUTHORIZED` (401), `FORBIDDEN` (403),
        `RATE_LIMITED` (429), `VALIDATION_ERROR` (400 — response carries a
        `details` array with Zod issues), `INTERNAL_ERROR` (500).


        **Agency master-key org selection** — `ORGANIZATION_REQUIRED` (400 — an
        agency master key must pass the `x-octolens-organization-id` header to
        select a target workspace), `FORBIDDEN_ORGANIZATION` (403 — the
        requested organization does not belong to the key's agency).


        **Plan / quota walls** — `UPGRADE_REQUIRED` (403 — an Agents-plan wall:
        a cap was hit (the lifetime search allowance or the monthly mention
        quota), or the endpoint is not part of the Agents plan at all — on that
        plan only search, org/usage/auth introspection, the upgrade link and
        monitoring recommendations are available, and every other endpoint
        answers this code before validating input; the envelope carries two
        ADDITIVE fields alongside the standard three, `upgradeUrl` (the upgrade
        page) and `upgradeCommand` (`octolens upgrade`, which mints a single-use
        authenticated link into that page), so an agent gets an actionable next
        step, not just prose), `QUOTA_EXCEEDED` (402 — the monthly mention quota
        is exhausted on a non-Agents plan; enable flex pricing or upgrade). The
        search routes also send `X-Octolens-Searches-Remaining` /
        `X-Octolens-Mentions-Remaining` response headers on success so clients
        can self-throttle before either wall.


        **Not found (404)** — generic `NOT_FOUND` plus domain-specific variants:
        `FEED_NOT_FOUND`, `KEYWORD_NOT_FOUND`, `KEYWORDS_NOT_FOUND`,
        `POST_NOT_FOUND`, `MENTION_NOT_FOUND`, `SUMMARY_NOT_FOUND`,
        `SEARCH_NOT_FOUND`, `SUGGESTION_NOT_FOUND`, `COMPANY_NOT_FOUND`,
        `ORG_NOT_FOUND`, `SETTINGS_NOT_FOUND`, `NOTIFICATION_NOT_FOUND`,
        `VIEW_NOT_FOUND`, `ALERT_NOT_FOUND`. A resource id that exists but
        belongs to another workspace answers the SAME not-found as an unused id
        — existence is never leaked across tenants.


        **Authorization (403)** — generic `FORBIDDEN` (the caller is not
        permitted to perform the operation, e.g. insufficient API-key scope).
        There are deliberately NO per-resource 403 codes: cross-workspace access
        reads as the resource's 404.


        **Business-rule violations (400)** — `INVALID_INPUT` (semantically
        invalid arguments), `KEYWORD_LIMIT_EXCEEDED` (plan cap hit),
        `LAST_ADMIN` (refuses to remove the only admin), `ITEM_EXISTS`
        (duplicate), `INVALID_DOMAIN`, `INVALID_TIMEZONE`, `SLACK_NOT_CONNECTED`
        / `SLACK_CHANNEL_NOT_FOUND` / `SLACK_CHANNEL_REQUIRED` (Slack
        destination validation), `WEBHOOK_URL_INVALID` (webhook URL rejected),
        `NO_DESTINATIONS` / `NO_SAMPLE_MENTION` (notification test delivery
        preconditions), `INVALID_DESTINATION` (a destination is
        missing/mismatched its type-specific sub-object, or two share a type) /
        `DESTINATION_INDEX_OUT_OF_RANGE` (feed destination index).


        **Operation failures (500)** — `ENGAGE_FAILED` /
        `RELEVANCE_UPDATE_FAILED` carry a specific code so clients can
        distinguish a failed mutation from a generic `INTERNAL_ERROR`.
      type: string
      enum:
        - UNAUTHORIZED
        - FORBIDDEN
        - RATE_LIMITED
        - VALIDATION_ERROR
        - INTERNAL_ERROR
        - ORGANIZATION_REQUIRED
        - FORBIDDEN_ORGANIZATION
        - MASTER_KEY_REQUIRED
        - AGENCY_INACTIVE
        - NOT_AN_AGENCY_WORKSPACE
        - AGENCY_NOT_FOUND
        - WORKSPACE_NOT_IN_AGENCY
        - ANCHOR_WORKSPACE
        - AGENCY_WORKSPACE_LIMIT
        - INVITE_FANOUT_TOO_LARGE
        - SUPERADMIN_REMOVE_FORBIDDEN
        - USER_REQUIRED
        - FLEX_REQUIRES_CAP
        - FLEX_REQUIRES_BUDGET
        - AGENCY_BILLING_NOT_CONFIGURED
        - WORKSPACE_CREATE_FAILED
        - WORKSPACE_SETUP_FAILED
        - WORKSPACE_ACCESS_FAILED
        - UPGRADE_REQUIRED
        - QUOTA_EXCEEDED
        - NOT_FOUND
        - FEED_NOT_FOUND
        - KEYWORD_NOT_FOUND
        - KEYWORDS_NOT_FOUND
        - POST_NOT_FOUND
        - MENTION_NOT_FOUND
        - SUMMARY_NOT_FOUND
        - SEARCH_NOT_FOUND
        - SUGGESTION_NOT_FOUND
        - COMPANY_NOT_FOUND
        - ORG_NOT_FOUND
        - SETTINGS_NOT_FOUND
        - NOTIFICATION_NOT_FOUND
        - VIEW_NOT_FOUND
        - ALERT_NOT_FOUND
        - INVALID_INPUT
        - KEYWORD_LIMIT_EXCEEDED
        - LAST_ADMIN
        - ITEM_EXISTS
        - INVALID_DOMAIN
        - INVALID_TIMEZONE
        - SLACK_NOT_CONNECTED
        - SLACK_CHANNEL_NOT_FOUND
        - SLACK_CHANNEL_REQUIRED
        - WEBHOOK_URL_INVALID
        - NO_DESTINATIONS
        - NO_SAMPLE_MENTION
        - INVALID_DESTINATION
        - DESTINATION_INDEX_OUT_OF_RANGE
        - ENGAGE_FAILED
        - RELEVANCE_UPDATE_FAILED
  securitySchemes:
    ApiKey:
      type: http
      scheme: bearer
      description: >-
        Clerk API key. Create one in Settings → API Keys. Pass as
        `Authorization: Bearer <key>`.

````