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

# Update an entity watch

> Updates an entity watch in place. `status`, `entities`, `config`, and `notifications` are
mutable, so the watched list is yours to edit without recreating the watch. `track` and
`fields` are fixed at creation and sending either returns `400`.

People or companies added by a `PATCH` are baselined silently on the next run: they establish
their own starting snapshot first, so adding a subject never fires a spurious notification.

A cancelled watch cannot be modified or reactivated.




## OpenAPI

````yaml /openapi-specs/2025-11-01/watch.yaml patch /watch/{dataset}/{watch_id}
openapi: 3.0.3
info:
  title: Watch API
  version: '2025-11-01'
  description: >
    The Watch API turns a Crustdata query into a recurring feed. A watch runs on
    your schedule and

    delivers only what is new or what changed since its previous run, to a
    webhook, Slack, Google Chat,

    or email.


    There are two kinds of watch, and the kind decides the URL:


    - **Discovery watches** (`/watch/{dataset}/search`) re-run a *search filter*
    and deliver records that
      newly match it. Use one to find people, companies, or jobs you do not know yet. Available for
      `person`, `company`, and `job`.
    - **Entity watches** (`/watch/{dataset}`) monitor a *list you supply* and
    deliver a notification when
      a tracked field on one of those records changes. Use one to follow a known set. Available for
      `person` and `company`.

    Both kinds share the same `config`, `notifications`, and run-history
    surfaces, and both start with a

    free baseline run: a discovery watch delivers a sample of up to 5 current
    matches, an entity watch

    records each subject's starting values and never fires. Every request needs
    an `Authorization` Bearer

    API key and the `x-api-version: 2025-11-01` header.


    **Credits.** A discovery watch is charged per new record delivered: 0.5 for
    a person, 2 for a company,

    0.5 for a job. An entity watch is charged per notification, tiered by
    `config.refresh_frequency_days`:

    5 credits at the 30-day default, 10 at 14 days, 20 at 7 days, 50 at 3 days,
    150 at 1 day. A run that

    finds nothing costs nothing, and the baseline run is free for both kinds.


    **Rate limit.** Watch-management requests are limited to 10 requests per
    minute per API key.


    **Access.** Creating a watch is open to any authenticated API customer, and
    the 10-requests-per-minute

    limit is what bounds abuse. What is gated is the data underneath: a create
    is rejected with `403` when

    the key is not entitled to the dataset, or when `fields` or `track` name
    field groups the key may not

    receive. Contact info@crustdata.com to request access.
servers:
  - url: https://api.crustdata.com
    description: Production API server
security:
  - bearerAuth: []
tags:
  - name: Watch APIs
    description: Recurring feeds over the Crustdata datasets
  - name: Discovery Watches
    description: Watches that re-run a search filter and deliver newly matching records
  - name: Entity Watches
    description: >-
      Watches that monitor a supplied list of people or companies for field
      changes
  - name: Watch Runs
    description: Run history and per-run delivery detail, shared by both watch kinds
paths:
  /watch/{dataset}/{watch_id}:
    patch:
      tags:
        - Watch APIs
        - Entity Watches
      summary: Edit the watched list, pause, or reconfigure an entity watch
      description: >
        Updates an entity watch in place. `status`, `entities`, `config`, and
        `notifications` are

        mutable, so the watched list is yours to edit without recreating the
        watch. `track` and

        `fields` are fixed at creation and sending either returns `400`.


        People or companies added by a `PATCH` are baselined silently on the
        next run: they establish

        their own starting snapshot first, so adding a subject never fires a
        spurious notification.


        A cancelled watch cannot be modified or reactivated.
      operationId: updateEntityWatch
      parameters:
        - $ref: '#/components/parameters/EntityDatasetPath'
        - $ref: '#/components/parameters/WatchId'
        - $ref: '#/components/parameters/ApiVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EntityWatchUpdateRequest'
            examples:
              add_subjects:
                summary: Replace the watched list with a longer one
                value:
                  entities:
                    professional_network_profile_urls:
                      - https://www.linkedin.com/in/sherryrobinson
                      - https://www.linkedin.com/in/williamhgates
              pause:
                summary: Pause the watch
                value:
                  status: paused
              fresher_data:
                summary: Move to a fresher, more expensive tier
                value:
                  config:
                    trigger:
                      type: interval
                      every_hours: 24
                    refresh_frequency_days: 3
      responses:
        '200':
          description: The updated watch.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EntityWatch'
        '400':
          description: >-
            An immutable field was sent, the body failed validation, or the
            watch is cancelled.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/SimpleErrorResponse'
                  - $ref: '#/components/schemas/ValidationErrorResponse'
              examples:
                immutable_track:
                  summary: Tried to change what the watch fires on
                  value:
                    non_field_errors:
                      - '''track'' cannot be changed after creation.'
                cancelled:
                  summary: The watch was already cancelled
                  value:
                    error: >-
                      This watch is cancelled and can no longer be modified.
                      Create a new watch instead.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/WatchForbidden'
        '404':
          $ref: '#/components/responses/WatchNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  parameters:
    EntityDatasetPath:
      name: dataset
      in: path
      required: true
      description: >
        The dataset the watch is built on. Entity watches support `person` and
        `company`; there is no

        job entity watch, and `job` returns `404` here. Use `/watch/job/search`
        for jobs.
      schema:
        type: string
        enum:
          - person
          - company
        example: person
    WatchId:
      name: watch_id
      in: path
      required: true
      description: The watch's `id`, returned when it was created.
      schema:
        type: integer
        example: 46936
    ApiVersion:
      name: x-api-version
      in: header
      required: true
      schema:
        type: string
        enum:
          - '2025-11-01'
        default: '2025-11-01'
        example: '2025-11-01'
      description: >
        API version to use. `2025-11-01` is the only accepted value. Send it on
        every call: it is

        enforced today on `POST /watch/{dataset}/search`, where a missing or
        unrecognized value returns

        `400`, and the other watch routes accept a request without it rather
        than relying on that.
  schemas:
    EntityWatchUpdateRequest:
      type: object
      description: >
        Body for updating an entity watch. Only these four keys are accepted.
        Sending `track`, `fields`,

        `dataset`, `kind`, `api_version`, `filters`, `sorts`, or `on` returns
        `400`.
      properties:
        status:
          type: string
          enum:
            - active
            - paused
            - cancelled
          example: paused
        entities:
          $ref: '#/components/schemas/EntitySubjects'
        config:
          $ref: '#/components/schemas/EntityWatchConfig'
        notifications:
          type: array
          items:
            $ref: '#/components/schemas/NotificationChannel'
    EntityWatch:
      type: object
      description: An entity watch, as returned by create, get, list, and update.
      properties:
        id:
          type: integer
          example: 46936
        kind:
          type: string
          enum:
            - entity
          example: entity
        dataset:
          type: string
          enum:
            - person
            - company
          example: person
        api_version:
          type: string
          example: '2025-11-01'
        config_version:
          type: string
          example: '2025-11-01'
        status:
          type: string
          enum:
            - active
            - paused
            - suspended
            - expired
            - cancelled
          example: active
        entities:
          $ref: '#/components/schemas/EntitySubjects'
        track:
          description: The saved track tree.
          oneOf:
            - $ref: '#/components/schemas/TrackCondition'
            - $ref: '#/components/schemas/TrackConditionGroup'
        fields:
          type: array
          nullable: true
          items:
            type: string
        config:
          $ref: '#/components/schemas/EntityWatchConfig'
        notifications:
          type: array
          items:
            $ref: '#/components/schemas/NotificationChannel'
        created_at:
          type: string
          format: date-time
          example: '2026-07-16T03:16:38.351849Z'
        last_run_at:
          type: string
          format: date-time
          nullable: true
          example: '2026-07-16T13:39:18.598964Z'
        notifications_count:
          type: integer
          example: 12
        credits_consumed:
          type: number
          example: 60
        last_notified_at:
          type: string
          format: date-time
          nullable: true
          example: '2026-07-16T13:39:18.598964Z'
    SimpleErrorResponse:
      type: object
      description: Error payload with a single human-readable message.
      required:
        - error
      example:
        error: Watch not found
      properties:
        error:
          type: string
          description: Human-readable description of what went wrong.
          example: Watch not found
    ValidationErrorResponse:
      type: object
      description: >
        Field-level validation errors, keyed by the request field that failed. A
        whole-body rule

        reports under `non_field_errors`. The message names the offending path
        inside a filter or track

        tree, for example `filters.conditions[1] is missing 'value'.`
      additionalProperties:
        type: array
        items:
          type: string
      example:
        non_field_errors:
          - 'Unknown filter operator at filters.conditions[0]: ''contains''.'
    EntitySubjects:
      type: object
      description: >
        The records to watch, as a map of identifier type to values. The
        accepted keys are exactly the

        ones the dataset's enrich endpoint accepts, and they are resolved to
        canonical ids when the

        watch runs. One watch holds up to 50,000 subjects across all keys.


        People accept `professional_network_profile_urls` and `business_emails`.
        Companies accept

        `domains`, `names`, `professional_network_profile_urls`, and
        `crustdata_company_ids`. Company

        ids must be positive integers; every other identifier is a non-empty
        string.
      minProperties: 1
      properties:
        professional_network_profile_urls:
          type: array
          minItems: 1
          items:
            type: string
          description: Profile URLs. Valid for both `person` and `company`.
          example:
            - https://www.linkedin.com/in/sherryrobinson
        business_emails:
          type: array
          minItems: 1
          items:
            type: string
          description: Work email addresses. `person` only.
          example:
            - jane@example.com
        domains:
          type: array
          minItems: 1
          items:
            type: string
          description: Company domains. `company` only.
          example:
            - netflix.com
            - stripe.com
        names:
          type: array
          minItems: 1
          items:
            type: string
          description: Company names. `company` only.
          example:
            - Netflix
        crustdata_company_ids:
          type: array
          minItems: 1
          items:
            type: integer
          description: Crustdata company ids, the exact identifier. `company` only.
          example:
            - 631394
      additionalProperties: false
    EntityWatchConfig:
      type: object
      description: Schedule, per-run caps, and data freshness for an entity watch.
      required:
        - trigger
      properties:
        trigger:
          $ref: '#/components/schemas/WatchTrigger'
        max_results_per_run:
          type: integer
          minimum: 1
          maximum: 1000
          default: 25
          description: Most notifications to deliver in one run.
          example: 100
        refresh_frequency_days:
          type: integer
          minimum: 1
          maximum: 30
          default: 30
          nullable: true
          description: >
            How fresh the tracked data is kept, in days. This is independent of

            `trigger.every_hours`: `every_hours` is how often the watch checks
            and notifies, while

            this is how up to date the data it checks against is, which is what
            decides how quickly

            a real change is detected.


            Fresher data costs more per notification: 5 credits at 30 days (the
            default), 10 at 14,

            20 at 7, 50 at 3, 150 at 1. A value between two tiers is billed at
            the fresher tier.

            Only fields backed by a refresh asset can be kept fresh this way, so
            a tracked field

            with no backing asset makes the create fail. Company watches have a
            higher minimum than

            person watches.
          example: 7
        preferred_hour:
          type: integer
          minimum: 0
          maximum: 23
          nullable: true
          description: Hour of day, in UTC, to prefer for the run.
          example: 9
        expires_at:
          type: string
          format: date
          nullable: true
          description: Auto-expire date, `YYYY-MM-DD`.
          example: '2027-01-01'
    NotificationChannel:
      description: >-
        One delivery channel. Every channel on a watch receives every
        notification.
      oneOf:
        - $ref: '#/components/schemas/WebhookChannel'
        - $ref: '#/components/schemas/SlackChannel'
        - $ref: '#/components/schemas/GoogleChatChannel'
        - $ref: '#/components/schemas/EmailChannel'
      discriminator:
        propertyName: type
        mapping:
          webhook:
            $ref: '#/components/schemas/WebhookChannel'
          slack:
            $ref: '#/components/schemas/SlackChannel'
          google_chat:
            $ref: '#/components/schemas/GoogleChatChannel'
          email:
            $ref: '#/components/schemas/EmailChannel'
    TrackCondition:
      type: object
      description: >
        One `track` leaf: the change on one field that fires a notification. The
        operator must match the

        field's shape, so `changed` needs a scalar path and `added` needs an
        array path. Mixing them

        returns `400`.
      required:
        - field
        - type
      properties:
        field:
          type: string
          description: >
            Dot-path into the record, addressing the same structure the
            dataset's enrich endpoint

            returns. A person profile has more than 200 addressable fields and
            any of them can be

            tracked.
          example: experience.employment_details.current
        type:
          type: string
          enum:
            - changed
            - added
            - '='
            - '!='
            - '>'
            - <
            - '=>'
            - '=<'
          description: >
            `changed` fires when a scalar becomes different from the last
            snapshot. `added` fires

            when a new element appears in an array. The comparison operators
            fire when the

            comparison flips from false to true, for example
            `professional_network.connections`

            crossing 500.
          example: added
        value:
          description: >-
            The threshold to compare against. Required for the comparison
            operators, omitted for `changed` and `added`.
          example: 500
    TrackConditionGroup:
      type: object
      description: >
        A group of track conditions. An `or` group fires if any child fires. An
        `and` group fires only

        when all conditions hold and at least one just became true. Groups nest.
      required:
        - op
        - conditions
      properties:
        op:
          type: string
          enum:
            - and
            - or
          example: or
        conditions:
          type: array
          minItems: 1
          items:
            oneOf:
              - $ref: '#/components/schemas/TrackCondition'
              - $ref: '#/components/schemas/TrackConditionGroup'
    AuthErrorResponse:
      type: object
      description: >
        Authentication error payload. The message distinguishes the two
        failures: `Missing API key in

        request` when no `Authorization` header was sent, and `Invalid API key
        in request` when the

        Bearer token is not a valid key.
      required:
        - message
      example:
        message: Invalid API key in request
      properties:
        message:
          type: string
          description: Human-readable authentication error message.
          example: Invalid API key in request
    StructuredErrorResponse:
      type: object
      description: >-
        Structured error payload, used for rate-limit and API-version failures.
        The flatter `SimpleErrorResponse` shape is used elsewhere, so parse
        defensively.
      required:
        - error
      example:
        error:
          type: rate_limit_error
          message: >-
            Rate limit exceeded for this endpoint. Please write to
            gtm@crustdata.co.
          metadata: []
      properties:
        error:
          type: object
          required:
            - type
            - message
          properties:
            type:
              type: string
              description: Machine-readable error type identifier.
              example: rate_limit_error
            message:
              type: string
              description: Human-readable description of what went wrong.
              example: >-
                Rate limit exceeded for this endpoint. Please write to
                gtm@crustdata.co.
            metadata:
              type: array
              description: Extra context, empty on most errors.
              items:
                type: object
                additionalProperties: true
              example: []
    WatchTrigger:
      type: object
      description: How often the watch runs. Interval is the only trigger type today.
      required:
        - type
        - every_hours
      properties:
        type:
          type: string
          enum:
            - interval
          description: Trigger type.
          example: interval
        every_hours:
          type: integer
          minimum: 1
          description: Hours between runs, for example `1`, `6`, `24`, or `168`.
          example: 24
    WebhookChannel:
      type: object
      description: >-
        An HTTPS endpoint you control. Deliveries are signed, and a URL that
        resolves to a private, loopback, or link-local address is rejected.
      required:
        - type
        - url
      properties:
        type:
          type: string
          enum:
            - webhook
          example: webhook
        url:
          type: string
          format: uri
          description: Public `http(s)` URL to POST each notification to.
          example: https://your-app.com/webhooks/crustdata
        headers:
          type: object
          additionalProperties:
            type: string
          description: >-
            Extra headers to send with each delivery, for routing or
            authentication on your side.
          example:
            X-Watch-Name: ml-engineers-paris
    SlackChannel:
      type: object
      description: >-
        A Slack incoming webhook. The URL must be a genuine
        `https://hooks.slack.com/services/...` webhook; any other URL fails
        delivery.
      required:
        - type
        - webhook_url
      properties:
        type:
          type: string
          enum:
            - slack
          example: slack
        webhook_url:
          type: string
          format: uri
          example: https://hooks.slack.com/services/T000/B000/XXXX
    GoogleChatChannel:
      type: object
      description: A Google Chat incoming webhook.
      required:
        - type
        - url
      properties:
        type:
          type: string
          enum:
            - google_chat
          example: google_chat
        url:
          type: string
          format: uri
          example: https://chat.googleapis.com/v1/spaces/AAAA/messages?key=...
    EmailChannel:
      type: object
      description: One or more email recipients.
      required:
        - type
        - to
      properties:
        type:
          type: string
          enum:
            - email
          example: email
        to:
          type: array
          minItems: 1
          items:
            type: string
            format: email
          description: Recipient addresses.
          example:
            - you@company.com
  responses:
    Unauthorized:
      description: The API key is missing or not valid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AuthErrorResponse'
          examples:
            invalid_api_key:
              summary: Bearer token present but not a valid key
              value:
                message: Invalid API key in request
            missing_api_key:
              summary: No Authorization header at all
              value:
                message: Missing API key in request
    WatchForbidden:
      description: >
        The key is not entitled to this dataset, or to a field group the request
        names. The watch

        surface itself is open to any authenticated API customer.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SimpleErrorResponse'
          example:
            error: >-
              You do not have access to the person dataset. Contact
              info@crustdata.com to request access.
    WatchNotFound:
      description: No watch with that id belongs to this key on this dataset.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SimpleErrorResponse'
          example:
            error: Watch not found
    RateLimited:
      description: >
        Too many watch requests. The per-endpoint limit is 10 requests per
        minute per API key, which is

        what bounds bursty create and update loops. Steady use is unaffected.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/StructuredErrorResponse'
          example:
            error:
              type: rate_limit_error
              message: >-
                Rate limit exceeded for this endpoint. Please write to
                gtm@crustdata.co.
              metadata: []
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key passed as a Bearer token in the Authorization header.

````

## Related topics

- [Company Entity Watcher](/watcher-docs/company/entity.md)
- [Person Entity Watcher](/watcher-docs/person/entity.md)
- [Delete an entity watch](/api-reference/watch-apis/delete-an-entity-watch.md)
- [Update a discovery watch](/api-reference/watch-apis/pause-resume-or-reconfigure-a-discovery-watch.md)
- [List entity watches](/api-reference/watch-apis/list-your-entity-watches.md)
