For the complete documentation index, see llms.txt. This page is also available as Markdown.

ClickUp

Configure and test ClickUp webhook delivery to ZenML.

The ClickUp webhook provider authenticates Workspace webhook deliveries and maps supported ClickUp payloads to a small catalog of semantic events. Consumers can use this provider-owned event model instead of depending on ClickUp's raw payload structure. Webhook triggers are the currently documented consumer that exposes this event configuration.

ClickUp registers webhooks through its API and generates the signing secret itself. Create the ZenML webhook first so you have an intake URL, then store ClickUp's secret on the ZenML webhook.

Create the webhook in ZenML

Create a ClickUp webhook in the active project:

zenml webhook create clickup-ops --type clickup

Describe the webhook and copy its endpoint URL:

zenml webhook describe clickup-ops

Via the SDK:

from zenml.client import Client
from zenml.webhooks.providers import BuiltinWebhookType

client = Client()
result = client.create_webhook(
    name="clickup-ops",
    webhook_type=BuiltinWebhookType.CLICKUP,
)

endpoint_url = result.endpoint_url

You can ignore the generated ZenML secret. ClickUp will issue a different shared secret when you register the endpoint, and that is the value ZenML must verify. See Create a webhook for secret-management and rotation options.

Register the webhook in ClickUp

ClickUp does not provide a repository-style webhook settings page. Create the subscription with the Create Webhook API using a personal API token and your ClickUp Workspace ID.

  1. In ClickUp, open Settings and create a personal API token.

  2. Copy the Workspace ID for the Workspace that should send events. You can also list authorized Workspaces with GET https://api.clickup.com/api/v2/team and use the id of the Workspace you want.

  3. Send a POST request that points ClickUp at the ZenML endpoint URL.

Copy webhook.secret from the response. You can optionally include space_id, folder_id, list_id, or task_id so ClickUp only sends events from one location. ZenML trigger filters still apply to whatever ClickUp delivers.

ClickUp sends an X-Signature header containing a raw hexadecimal HMAC-SHA256 digest of the exact request body. There is no sha256= prefix. ZenML verifies this signature before it parses or forwards the delivery.

Store ClickUp's signing secret

Rotate the ZenML webhook secret to the value ClickUp returned. Until this step succeeds, real ClickUp deliveries fail authentication with 401 Unauthorized. ClickUp suspends the webhook after that response.

Via the SDK:

Supported events and filters

The provider reads the ClickUp event name from the JSON event field and maps qualifying payloads to ZenML semantic events with the same names:

ClickUp event
ZenML semantic event
Available filter fields

Task created

taskCreated

task_id, list_id, space_id, folder_id

Task updated

taskUpdated

task_id, list_id, space_id, folder_id

Task deleted

taskDeleted

task_id, list_id, space_id, folder_id

Task status updated

taskStatusUpdated

task_id, list_id, space_id, folder_id, status

Task moved

taskMoved

task_id, list_id, space_id, folder_id

Task assignee updated

taskAssigneeUpdated

task_id, list_id, space_id, folder_id

Task comment posted

taskCommentPosted

task_id, list_id, space_id, folder_id

List created

listCreated

list_id, space_id, folder_id

List updated

listUpdated

list_id, space_id, folder_id

List deleted

listDeleted

list_id, space_id, folder_id

Event names match ClickUp's camelCase identifiers. Resource IDs are compared as strings, even when ClickUp sends a numeric list_id, space_id, or folder_id. Each event type has its own typed target class, such as TaskStatusUpdated or ListCreated, so filters that do not apply to that event are rejected at configuration time.

status is only available on taskStatusUpdated. It is the post-change value from the last history_items entry: a string after value, or after.status when after is an object. If you filter on status and the payload has no after-value, the event does not match. List events do not accept task_id, and task events other than taskStatusUpdated do not accept status.

A ClickUp event that is missing from this catalog can still authenticate and return 202 Accepted. It is not mapped to a semantic event, so webhook triggers do not match it. A missing or empty event or webhook_id field returns 400 Bad Request.

String filters

Semantic event fields use ZenML string filters (StringFilterOption). You can use:

  • a plain string for exact equality, such as list_id: "162641285";

  • equals:, notequals:, contains:, notcontains:, startswith:, and endswith: expressions;

  • oneof: with a non-empty JSON list for exact alternatives, such as status: 'oneof:["done","complete"]';

  • notoneof: with a non-empty JSON list for exclusions; and

  • a YAML list to OR multiple expressions for one field.

All filter fields in the table support these generic webhook string operators. Values configured for the same field use OR logic; different populated fields use AND logic. Omitted fields match any value. If a configured field is absent from a delivery, it does not match. See the complete string filter operator reference.

For example, this target matches status updates to done on one list:

See Webhook triggers for a complete example that uses this configuration to execute a pipeline snapshot.

Mock a signed ClickUp delivery

You can test the endpoint without asking ClickUp to send a delivery. This example uses a minimal ClickUp-like status payload; the important part is signing and sending exactly the same bytes.

Set the endpoint and the ClickUp signing secret stored on the ZenML webhook:

Write the request body without adding a trailing newline:

Calculate the raw hexadecimal signature over the file's exact bytes:

Send those same bytes with the ClickUp signature header:

A valid delivery returns 202 Accepted. This confirms webhook intake, not that a consumer matched or a pipeline run started.

To confirm that signature verification is active, send the same body with an invalid signature:

This request returns 401 Unauthorized. If a calculated signature fails, verify that the bytes passed to --data-binary are the bytes used to calculate the digest; even a whitespace or newline change produces a different signature. Do not prefix the digest with sha256=.

ClickUp immediately suspends a webhook after an unauthorized response, so align the signing secret before ClickUp starts delivering events.

Last updated

Was this helpful?