---
title: "Update a Goal"
method: PATCH
path: "/v1/web-visits/goals/{id}"
tags: ["Web Visits", "v1"]
---

# Update a Goal

`PATCH /v1/web-visits/goals/{id}`

## Request

- Base URL: `https://api.leadfeeder.com`
- URL: `https://api.leadfeeder.com/v1/web-visits/goals/{id}`
- Auth: one of:
  - API key in header `X-Api-Key`
  - OAuth 2
    - Authorization code: `https://api.leadfeeder.com/v1/oauth/authorize`
    - Token URL: `https://api.leadfeeder.com/v1/oauth/token` scopes: `web_visits:write`

Update an existing goal. All attributes are optional; only the attributes you send are changed. `criterion` replaces the stored criterion: send its `operator`, and a `value` unless the operator is `is_present` or `is_empty`, which take none. Its `field` can be omitted and cannot be changed — delete the goal and create a new one instead. Changes are not retroactive: page views the goal already marked keep it, and earlier page views are not re-checked.

The `operator` must be one the goal's `field` accepts; an unsupported one is rejected with a 422 naming the accepted operators:

| `field` | Operators |
| --- | --- |
| `page_url` | `is`, `is_not`, `begins_with`, `not_begins_with`, `ends_with`, `contains`, `not_contains` |
| `page` | `is`, `is_not`, `begins_with`, `ends_with`, `contains`, `not_contains` |
| `form_submit` | `is`, `is_not`, `is_present`, `is_empty` |
| `file_download_name` | `is`, `is_not`, `contains`, `is_present`, `is_empty` |
| `video_title` | `is`, `is_not`, `begins_with`, `contains`, `is_present`, `is_empty` |
| `video_url` | `is`, `is_not`, `begins_with`, `is_present`, `is_empty` |

:::info
Requires the `web_visits:write` OAuth2 scope.
:::

## Path parameters

- `id` string, required

## Query parameters

- `account_id` string, required

## Request body

- GoalUpdateRequestV1
  - `data` object, required
    - `attributes` object, required
      - `criterion` union: The page-view condition that marks a page view as meeting the goal. Each `field` accepts its own set of operators; the `anyOf` branches list them.
        - object: Compares the page URL with a non-empty `value`.
          - `field` 'page_url': What the goal's condition looks at.
          - `operator` 'is' | 'is_not' | 'begins_with' | 'not_begins_with' | 'ends_with' | 'contains' | 'not_contains', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable, required: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
        - object: Compares the page title with a non-empty `value`.
          - `field` 'page': What the goal's condition looks at.
          - `operator` 'is' | 'is_not' | 'begins_with' | 'ends_with' | 'contains' | 'not_contains', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable, required: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
        - object: Compares the URL of the page a form was submitted on with a non-empty `value`.
          - `field` 'form_submit': What the goal's condition looks at.
          - `operator` 'is' | 'is_not', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable, required: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
        - object: Matches whether the page view is a form submission at all; takes no `value` (omit it or send `null`).
          - `field` 'form_submit': What the goal's condition looks at.
          - `operator` 'is_present' | 'is_empty', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
        - object: Compares the downloaded file's name with a non-empty `value`.
          - `field` 'file_download_name': What the goal's condition looks at.
          - `operator` 'is' | 'is_not' | 'contains', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable, required: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
        - object: Matches whether the page view is a file download at all; takes no `value` (omit it or send `null`).
          - `field` 'file_download_name': What the goal's condition looks at.
          - `operator` 'is_present' | 'is_empty', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
        - object: Compares the video title with a non-empty `value`.
          - `field` 'video_title': What the goal's condition looks at.
          - `operator` 'is' | 'is_not' | 'begins_with' | 'contains', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable, required: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
        - object: Matches whether the page view has a video title at all; takes no `value` (omit it or send `null`).
          - `field` 'video_title': What the goal's condition looks at.
          - `operator` 'is_present' | 'is_empty', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
        - object: Compares the video URL with a non-empty `value`.
          - `field` 'video_url': What the goal's condition looks at.
          - `operator` 'is' | 'is_not' | 'begins_with', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable, required: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
        - object: Matches whether the page view has a video URL at all; takes no `value` (omit it or send `null`).
          - `field` 'video_url': What the goal's condition looks at.
          - `operator` 'is_present' | 'is_empty', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
          - `value` string, nullable: The value compared against. Required for every operator except `is_present` and `is_empty`, which take none. `contains`, `begins_with` and `ends_with` need some text besides `/` and `*`.
      - `enabled` boolean: Whether the goal is active. A disabled goal stops matching new page views. Left unchanged when omitted.
      - `name` string: The human-readable name of the goal.
    - `type` 'goal', required: The type of the object

## Response `200`

Success

- object
  - `data` GoalV1, required
    - `attributes` object, required
      - `created_at` string, date-time, required: Date and time when the goal was created
      - `criterion` object, required: The page-view condition that marks a page view as meeting the goal. It uses the same `field`, `operator` and `value` vocabulary as a custom feed's `advanced_filters` conditions.
        - `field` 'page_url' | 'page' | 'form_submit' | 'file_download_name' | 'video_title' | 'video_url', required: What the goal's condition looks at.
        - `operator` 'is' | 'is_not' | 'begins_with' | 'not_begins_with' | 'ends_with' | 'contains' | 'not_contains' | 'is_present' | 'is_empty', required: How `value` is compared. Each `field` accepts its own set of operators; an unsupported pairing is rejected with a 422 naming the accepted operators.
        - `value` string, nullable, required: The value compared against. `null` for `is_present` and `is_empty`, which take no value.
      - `enabled` boolean, required: Whether the goal is active. A disabled goal stops matching new page views; page views it already matched keep the goal.
      - `name` string, required: The human-readable name of the goal
      - `updated_at` string, date-time, required: Date and time when the goal was last updated
    - `id` string, required: The unique identifier for the goal
    - `relationships` object, required
      - `custom_feed` object, nullable, required: The Web Visits custom feed created for this goal, which lists the visiting companies with a page view meeting it. Pass its `id` as `custom_feed_id` to **Retrieve Web Visits Companies**. `null` until the feed has been created, shortly after the goal, and for goals created once the account already has 20 goal feeds.
        - `id` string, required: The ID of the custom feed.
        - `type` 'custom_feed', required
    - `type` 'goal', required: The type of the object
  - `meta` object, required
    - `request_id` string, required: A unique identifier assigned to each API request for end-to-end traceability.

## Other responses

- `400`: Bad Request
- `401`: Unauthorized. The request was rejected because the credentials are missing, invalid, expired, or have been revoked. The client must re-authenticate before retrying. Clients can differentiate via the `code` value of the first item in `errors`.
- `403`: Forbidden. The credentials are valid but the caller is not authorized to perform the request — typically because the user lacks the required permission, the OAuth scope is insufficient, the subscription plan does not include the required entitlement, the `account_id` does not belong to the authenticated user, or the target list is read-only and its members cannot be modified. Clients can differentiate via the `code` value of the first item in `errors`.
- `404`: Not found
- `422`: Unprocessable entity
- `429`: Too many requests. Either the per-second rate limit or the monthly quota configured for the API key / OAuth application has been exceeded. Clients can differentiate via the `code` value of the first item in `errors`.
- `500`: Internal server error
- `504`: Server timeout

## Changes

> 46 revisions in range; 1 not diffed.

- **2026-10-15** `65f17e3add9b`: 20 breaking
  - the `data/attributes/criterion/allOf[subschema #1]/anyOf[subschema #10: video_url: presence]/field` request property type changed from no type to `string`
  - the `data/attributes/criterion/allOf[subschema #1]/anyOf[subschema #10: video_url: presence]/operator` request property type changed from no type to `string`
  - the `data/attributes/criterion/allOf[subschema #1]/anyOf[subschema #1: page_url: comparison]/field` request property type changed from no type to `string`
  - the `data/attributes/criterion/allOf[subschema #1]/anyOf[subschema #1: page_url: comparison]/operator` request property type changed from no type to `string`
  - …16 more
- **2026-10-15** `9016d077b7d3`: 1 breaking, 1 info
  - removed `subschema #1, subschema #2` from the `data/attributes/criterion/allOf[subschema #1]/` request property `oneOf` list
  - added `subschema #1: page_url: comparison, subschema #2: page: comparison, subschema #3: form_submit: comparison, subschema #4: form_submit: presence, subschema #5: file_download_name: comparison, subschema #6: file_download_name: presence, subschema #7: video_title: comparison, subschema #8: video_title: presence, subschema #9: video_url: comparison, subschema #10: video_url: presence` to the `data/attributes/criterion/allOf[subschema #1]/` request property `anyOf` list
- **2026-10-14** `c0f1ebf5f542`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/leadfeeder/apis/leadfeeder-public-api/changes/v1/web-visits/goals/:id/patch.md)

---

[API](https://skmtc.dev/leadfeeder/apis/leadfeeder-public-api.md) - [All operations](https://skmtc.dev/leadfeeder/apis/leadfeeder-public-api/llms.txt) - [OpenAPI document](https://skmtc.dev/leadfeeder/apis/leadfeeder-public-api/revisions/686d4e39eb05?raw)
