# client.webhooks

> client.webhooks — the webhooks operations.

`client.webhooks` — the `webhooks` operations.

On `AsyncKindgi` every method is the same, awaited.

## `client.webhooks.list()`

```python signature
list(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    status: Literal['active', 'paused'] | None = None,
    project_id: str | UUID | None = None,
    timeout: float | None = None,
) -> WebhookTriggerCollectionPage
```

List webhook triggers. `GET /v1/webhooks`

The workspace's webhook triggers whose project the caller may read; with `projectId`, that project's only (which needs `read` on it).

## `client.webhooks.register()`

```python signature
register(
    body: RegisterWebhookTriggerBody | Mapping[str, Any] | None = None,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
    **fields: Any,
) -> WebhookTriggerRecord
```

Register a webhook trigger. `POST /v1/webhooks`

A signed request to the trigger's `receiveUrl` (`POST /v1/hooks/{tenantId}/{webhookId}`) starts a run of its flow, as its owner: the caller, until an admin takes it over. The route mints `webhookId` (a random UUID). The signing secret is written first (`POST /v1/secrets`), and `hmacSecretName` names it; a model provider's key is refused (`400 provider-key-refused`). Where it's read is recorded on the trigger (`hmacSecretScope`): the first of its project, the project's org and the workspace that has it and that the caller administers, else the broadest they administer; the receiver reads it there only. Or `generateSecret: true`: the server makes it, stores it as `hmacSecretName` in the trigger's own project (a name the project already has is refused, `409 secret-write-conflict`), and returns it once as `signingSecret`. `signature` says how the sender signs (default: a hex HMAC-SHA256 of the raw body in `X-Kindgi-Signature`); `deliveryIdHeader` names the header whose value, with the body, dedupes deliveries. A trigger that names no `projectId` goes in the workspace's default project. Needs `write` on the project and `execute` on the flow. `toolsAllowedUnattended` names the tools whose calls in its runs needn't wait for a person's yes, though a tool asks for one in a run no person started: naming it takes `admin` on the project, each id must be a tool the flow calls, and it's audited (`trigger-tools-allowed-unattended-set`).

## `client.webhooks.get()`

```python signature
get(trigger_id: str | UUID, /, *, timeout: float | None = None) -> WebhookTriggerRecord
```

Fetch a webhook trigger. `GET /v1/webhooks/{triggerId}`

## `client.webhooks.update()`

```python signature
update(
    trigger_id: str | UUID,
    body: PatchWebhookTriggerBody | Mapping[str, Any] | None = None,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
    **fields: Any,
) -> WebhookTriggerRecord
```

Update a webhook trigger. `PATCH /v1/webhooks/{triggerId}`

Changes the flow version (which needs `execute` on the flow, and must be a version the trigger's project runs), the input, the label, which secret signs (`hmacSecretName`, never a model provider's key), the signature scheme, the delivery-id header, the body cap or the rate (`null` clears the last three). The flow itself stays: register another trigger for another flow. Rotating the secret's value goes through `POST /v1/secrets/{name}/rotate`. `toolsAllowedUnattended` replaces the list (`[]` clears it; absent keeps it, a new flow version too): naming it takes `admin` on the trigger's project, it's checked against the flow at the version the trigger will run, and every change is audited. A webhook trigger is personal or shared, and no change moves its owner: only `POST /v1/webhooks/{triggerId}/owner` does. A trigger a person owns is personal: only they (holding `write` on its project) or a project admin may change it. A trigger a service account owns is shared: changing its flow version, its input, its `toolsAllowedUnattended` or `hmacSecretName` takes `admin` on its project, and the project's writers may change its label and delivery settings. Without an authorization store, a project admin here is a tenant-admin key. A change by anyone but a personal trigger's owner is audited as `trigger-changed-for-owner`, naming the fields changed (and the signing secret's name before and after) and the trigger's version after it, never their values.

## `client.webhooks.pause()`

```python signature
pause(
    trigger_id: str | UUID,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
) -> WebhookTriggerRecord
```

Pause a webhook trigger. `POST /v1/webhooks/{triggerId}/pause`

While paused, a signed delivery is answered `202` and recorded as a `skipped` fire, and no run starts: **its event is dropped**. Senders such as WooCommerce never resend, so resuming doesn't bring those events back. A personal trigger's owner or a project admin pauses it; a shared one, any writer of its project.

## `client.webhooks.resume()`

```python signature
resume(
    trigger_id: str | UUID,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
) -> WebhookTriggerRecord
```

Resume a webhook trigger. `POST /v1/webhooks/{triggerId}/resume`

A personal trigger's owner or a project admin resumes it; a shared one, any writer of its project.

## `client.webhooks.unregister()`

```python signature
unregister(
    trigger_id: str | UUID,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
) -> WebhookTriggerUnregisterResult
```

Soft-delete a webhook trigger (tombstone). `POST /v1/webhooks/{triggerId}/unregister`

A personal trigger's owner or a project admin deletes it; a shared one, any writer of its project.

## `client.webhooks.fires()`

```python signature
fires(
    trigger_id: str | UUID,
    /,
    *,
    limit: int | None = None,
    cursor: str | None = None,
    timeout: float | None = None,
) -> WebhookFirePage
```

A webhook trigger's deliveries. `GET /v1/webhooks/{triggerId}/fires`

Newest first: each delivery the trigger received and what came of it: the run it started (`started`, or `pending` while it starts), or why it was `skipped` (paused, its project or workspace suspended, or its workspace closing) or `refused` (`detail`: `signature-missing`, `signature-invalid`, `stale`, `secret-unavailable`, `unregistered`, `rate-limited`, `body-too-large`, `body-not-json`, `body-too-deep`, or the owner's lost access). A delivery that repeated an earlier one's dedupe key counts on that one (`duplicates`). A fire keeps the event only until its run starts. Refusals and skipped deliveries past 20 a minute only count (the trigger's `suppressedRefusals`).

## `client.webhooks.list_versions()`

```python signature
list_versions(
    trigger_id: str | UUID,
    /,
    *,
    limit: int | None = None,
    cursor: str | None = None,
    timeout: float | None = None,
) -> WebhookTriggerVersionPage
```

A webhook trigger's versions. `GET /v1/webhooks/{triggerId}/versions`

Newest first: each version of the webhook trigger, what it was from `createdAt` on, who made it (`createdBy`) and why (`cause`). Registering makes version 1, and each change makes the next (a PATCH that alters it, or a take-over to another owner): a PATCH that changes nothing makes none, nor do pausing, resuming or deleting. A version never changes once made. A delivery's run uses the newest when it starts, and keeps it whatever later edits make (the run's `trigger.version`). A webhook trigger from before versions has none until its next change or its first fire. The versions keep every input the webhook trigger has had, so a secret pasted into its input stays in an earlier version after you edit it out: rotate it. Whoever can read the webhook trigger can read its versions.

## `client.webhooks.get_version()`

```python signature
get_version(
    trigger_id: str | UUID,
    version: int,
    /,
    *,
    timeout: float | None = None,
) -> WebhookTriggerVersion
```

Fetch one version of a webhook trigger. `GET /v1/webhooks/{triggerId}/versions/{version}`

## `client.webhooks.take_ownership()`

```python signature
take_ownership(
    trigger_id: str | UUID,
    body: TakeTriggerOwnerBody | Mapping[str, Any] | None = None,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
    **fields: Any,
) -> WebhookTriggerRecord
```

Take over a webhook trigger. `POST /v1/webhooks/{triggerId}/owner`

The caller becomes the trigger's owner, so its runs act as the caller from the next delivery. Needs `admin` on the trigger's project (without an authorization store, a tenant-admin key) and `execute` on its flow. For a trigger whose owner left or lost access, or to make one shared or personal. It's the one way a trigger's owner changes, audited as `trigger-owner-set`, naming the trigger's version after it; a person it's taken from is told, where the server tells owners. Name `runAsServiceAccountId` instead to make a service account the owner: that needs `act_as` on the account (a tenant admin's), and the account must itself hold `execute` on the flow. With an authorization store, that's checked when the account is named: an account that can't is refused (`409 trigger-owner-cannot-run-target`). Or name `runAsProject: true` to make the project's own service account the owner, made the first time with the `editor` role on the project, so the trigger is shared and runs as the project. It runs only what the project's editors can, and is checked the same way. The trigger keeps its `toolsAllowedUnattended`, now for the new owner's runs; a body naming it is refused (change it with PATCH).
