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

# Preview match frequency for an alert before creating it

> Kicks off an asynchronous sweep over the past N days (default 7, max 30) and returns a preview_id. Poll GET /v1/alerts/preview/{previewId} until status is completed (or failed). The result includes the total match count, a per-day breakdown, and a small sample of the most-recent matches with full payload so the UI can show "if you create this alert, here's what it would have caught."



## OpenAPI

````yaml /openapi.json post /v1/projects/{projectId}/alerts/preview
openapi: 3.1.0
info:
  description: Public API for Particle — news intelligence, financial data, and analysis.
  title: Particle API
  version: 0.1.0
servers:
  - url: https://api.particle.pro
security:
  - ApiKeyHeader: []
  - BearerAuth: []
paths:
  /v1/projects/{projectId}/alerts/preview:
    post:
      tags:
        - Alerts
        - tier:standard
      summary: Preview match frequency for an alert before creating it
      description: >-
        Kicks off an asynchronous sweep over the past N days (default 7, max 30)
        and returns a preview_id. Poll GET /v1/alerts/preview/{previewId} until
        status is completed (or failed). The result includes the total match
        count, a per-day breakdown, and a small sample of the most-recent
        matches with full payload so the UI can show "if you create this alert,
        here's what it would have caught."
      operationId: preview-alert
      parameters:
        - description: Project ID.
          in: path
          name: projectId
          required: true
          schema:
            description: Project ID.
            type: string
        - description: Number of days back to sweep. Defaults to 7; capped at 30.
          explode: false
          in: query
          name: window_days
          schema:
            description: Number of days back to sweep. Defaults to 7; capped at 30.
            examples:
              - 7
            format: int64
            type: integer
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PreviewAlertBody'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewAlertResponseBody'
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/PlatformError'
          description: Error
      x-codeSamples:
        - label: cURL
          lang: curl
          source: |-
            curl -X POST \
              -H "X-API-Key: $PARTICLE_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{}' \
              "https://api.particle.pro/v1/projects/{projectId}/alerts/preview"
components:
  schemas:
    PreviewAlertBody:
      additionalProperties: false
      properties:
        entities:
          description: Same as CreateAlertBody.entities.
          items:
            $ref: '#/components/schemas/AlertEntity'
          maxItems: 100
          minItems: 1
          type:
            - array
            - 'null'
        filters:
          $ref: '#/components/schemas/AlertFilters'
          description: >-
            Same as CreateAlertBody.filters. Pass the filters you intend to save
            so the estimate reflects what the alert would actually surface. Only
            languages and speaker_roles narrow the historical sweep; relevance
            and source_popularity are read-time projections that don't run on
            historical episodes, so setting them leaves the count unchanged (the
            estimate is an upper bound when relevance=RELEVANT).
        kind:
          description: Same as CreateAlertBody.kind. Defaults to ENTITY_MENTION.
          enum:
            - ENTITY_MENTION
            - PODCAST_SPEAKER
          type: string
      required:
        - entities
      type: object
    PreviewAlertResponseBody:
      additionalProperties: false
      properties:
        preview_id:
          description: >-
            Deterministic preview ID — repeating the same body within the TTL
            returns the same ID. Poll GET /v1/alerts/preview/{previewId} when
            status is 'in_progress'.
          type: string
        status:
          description: >-
            'in_progress' means a sweep is running (or was just started);
            'completed' means an earlier identical preview is still cached and
            the result is ready to fetch via GET. A 'failed' prior preview is
            treated as a cache miss and a fresh sweep is started — callers never
            see 'failed' on this endpoint.
          enum:
            - in_progress
            - completed
          type: string
        window_days:
          format: int64
          type: integer
      required:
        - preview_id
        - status
        - window_days
      type: object
    PlatformError:
      additionalProperties: false
      properties:
        detail:
          description: >-
            A human-readable explanation specific to this occurrence of the
            problem.
          examples:
            - Property foo is required but is missing.
          type: string
        error_code:
          type: string
        errors:
          description: Optional list of individual error details
          items:
            $ref: '#/components/schemas/ErrorDetail'
          type:
            - array
            - 'null'
        instance:
          description: >-
            A URI reference that identifies the specific occurrence of the
            problem.
          examples:
            - https://example.com/error-log/abc123
          format: uri
          type: string
        resolve:
          $ref: '#/components/schemas/ErrorResolve'
        status:
          description: HTTP status code
          examples:
            - 400
          format: int64
          type: integer
        title:
          description: >-
            A short, human-readable summary of the problem type. This value
            should not change between occurrences of the error.
          examples:
            - Bad Request
          type: string
        type:
          default: about:blank
          description: A URI reference to human-readable documentation for the error.
          examples:
            - https://example.com/errors/example
          format: uri
          type: string
      type: object
    AlertEntity:
      additionalProperties: false
      properties:
        entity_id:
          type: string
        entity_type:
          enum:
            - PERSON
            - COMPANY
            - KNOWLEDGE_GRAPH_ENTITY
          type: string
        image_url:
          description: >-
            Image URL for the watched entity (headshot for PERSON, logo for
            COMPANY, image for KGE). Omitted from the response when no image is
            available.
          type: string
        name:
          description: >-
            Display name of the watched entity. Omitted from the response when
            the underlying record cannot be resolved (e.g. deleted).
          type: string
      required:
        - entity_type
        - entity_id
      type: object
    AlertFilters:
      additionalProperties: false
      properties:
        languages:
          description: >-
            Primary language tags the source episode must be in. Accepts a 2-3
            letter primary tag (e.g. 'en', 'de', 'sma') and tolerates an
            optional 2-4 char region or script subtag for readability (e.g.
            'pt-BR', 'zh-Hant'); matching is on the PRIMARY tag only, so 'pt-BR'
            surfaces every Portuguese episode regardless of region and 'pt-BR' +
            'pt-PT' collapse to one. Empty or missing means all languages are
            surfaced. Codes are compared case-insensitively; the API echoes back
            the canonical primary-tag form.
          items:
            type: string
          type:
            - array
            - 'null'
        relevance:
          description: >-
            EVERYTHING (the default) returns both on-target and incidental
            matches — the watched entity is correctly identified in both, only
            the depth of discussion differs. RELEVANT narrows to on-target only:
            matches where the watched entity is the subject being discussed,
            dropping passing mentions. Wrong-entity matches (knowledge-graph
            name collisions, e.g. the podcast 'Truckie' for an alert on the town
            'Truckee') are globally suppressed before any filter runs.
          enum:
            - EVERYTHING
            - RELEVANT
          type: string
        source_popularity:
          description: >-
            ANY (the default) keeps matches from every source. POPULAR keeps
            only matches whose source podcast scores in the top 5% by
            chart-popularity percentile (Podcast.Popularity >= 0.95; cume_dist
            over current chart entries, multi-region weighted). Podcasts that
            aren't currently charting drop out.
          enum:
            - ANY
            - POPULAR
          type: string
        speaker_roles:
          description: >-
            PODCAST_SPEAKER alerts only. REPLACES (not intersects with) the
            default appearance set the alert matches against. Default when
            omitted is GUEST, PANELIST, CORRESPONDENT, AUDIENCE,
            SOUNDBITE_SPEAKER — HOST is intentionally excluded because hosting
            the show isn't an appearance. Setting ['HOST'] flips that — only
            HOST appearances match. Setting ['GUEST'] alone narrows further.
            Sending this on an ENTITY_MENTION alert returns 422 — the wire layer
            can't tell at validation time, so the handler boundary rejects it
            once the parent kind is known. Persisted in canonical uppercase form
            (e.g. 'GUEST'); raw STT/LLM labels like CALLER or REPORTER are not
            accepted because podcast_episode_speakers.role canonicalizes to the
            buckets above at write time.
          items:
            enum:
              - HOST
              - GUEST
              - PANELIST
              - CORRESPONDENT
              - AUDIENCE
              - SOUNDBITE_SPEAKER
            type: string
          type:
            - array
            - 'null'
      type: object
    ErrorDetail:
      additionalProperties: false
      properties:
        location:
          description: >-
            Where the error occurred, e.g. 'body.items[3].tags' or
            'path.thing-id'
          type: string
        message:
          description: Error message text
          type: string
        value:
          description: The value at the given location
      type: object
    ErrorResolve:
      additionalProperties: false
      properties:
        action:
          type: string
        endpoint:
          type: string
        message:
          type: string
        method:
          type: string
        url:
          type: string
      required:
        - message
      type: object
  securitySchemes:
    ApiKeyHeader:
      description: Pass your API key in the X-API-Key header (recommended).
      in: header
      name: X-API-Key
      type: apiKey
    BearerAuth:
      description: Pass your API key as a Bearer token in the Authorization header.
      scheme: bearer
      type: http

````