# Update a webhook trigger

PATCH

/v1/webhooks/{triggerId}

```sh
curl --request PATCH \
  --url https://example.com/v1/webhooks/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0 \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{ "config": { "input": "example" }, "label": "example", "flowVersion": "example", "hmacSecretName": "example", "signature": { "kind": "hmac-sha256", "encoding": "hex", "header": "example", "prefix": "example" }, "deliveryIdHeader": "example", "bodyLimitBytes": 1, "rateLimitPerMinute": 1, "toolsAllowedUnattended": [ "example" ] }'
```

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.

## Authorizations

- **[bearerAuth](https://docs.kindgi.com/v0.3.1/reference/api.md#bearerauth)**

## Parameters

### Path Parameters

**triggerId**

required

string format: uuid

TriggerId — the trigger id (a UUID).

### Header Parameters

**Idempotency-Key**

string

\>= 1 characters

Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the tenant is their own request (from 0.1.5). Retries with the same key return the original response byte-identical (per `docs/API-ROUTE-CONVENTIONS.md` §3.1). A retry sent while the first request still runs is answered `409 idempotency-key-in-flight` with `Retry-After`, on a runtime whose store holds keys (from 0.1.5); retry after it, and you get the first request’s answer. An answer that carries a secret (a new API key or public run token, a session token, a generated signing secret) isn’t kept: a retry gets `409 idempotency-key-replay-withheld`, with the first request’s status and when it succeeded (from 0.1.5). A request whose key store can’t be read before it runs is answered `503 idempotency-store-unavailable` with `Retry-After`: nothing ran, so retry with the same key (from 0.3.0).

## Request Bodyrequired

application/json

object

**config**

object

**input**

**label**

string | null

**flowVersion**

string

\>= 1 characters

**hmacSecretName**

string

\>= 1 characters

**signature**

One of:

- **object**

  object

  **kind**

  required

  string

  Allowed values: hmac-sha256

  **encoding**

  required

  string

  Allowed values: hex base64

  **header**

  required

  The header that carries the signature (matched case-insensitively).

  string

  \>= 1 characters <= 100 characters

  **prefix**

  Stripped from the header’s value before decoding (e.g. `sha256=`).

  string

  \>= 1 characters <= 32 characters

- **object**

  object

  **kind**

  required

  string

  Allowed values: standard-webhooks

  **toleranceSeconds**

  integer

  \>= 1 <= 3600

**deliveryIdHeader**

`null` stops deduping.

string | null

<= 100 characters

**bodyLimitBytes**

`null`: the default.

integer | null

\>= 1024 <= 1048576

**rateLimitPerMinute**

`null`: the default.

integer | null

\>= 1 <= 6000

**toolsAllowedUnattended**

Replaces the trigger’s list; `[]` clears it, and absent keeps it (a new `flowVersion` too). Naming it takes `admin` on the trigger’s project, and it’s checked against the flow at the version the trigger will run. Each tool by its id, with no wildcard or pattern, and only tools this trigger’s flow or agent calls. It lifts only that wait: the tenant’s approval rules, the agent’s own rules, the permission check (as the trigger’s owner) and the tool’s own limits all still apply.

Array\<string>

<= 100 items unique items

## Responses

### 200

Updated webhook trigger.

application/json

A webhook trigger: a signed request to its `receiveUrl` starts a run of its flow, as its owner. The signing secret is never on it, only its name.

object

**triggerId**

required

string

**webhookId**

required

The routable id in the receive URL. Route-minted (a random UUID); unique per tenant. Not a secret: the signature is what a request is checked by.

string

**receiveUrl**

Where the sender posts: `{public URL}/v1/hooks/{tenantId}/{webhookId}`. Absent when the deployment has no public URL configured (`KINDGI_PUBLIC_URL`); it’s never taken from a request’s `Host`.

string format: uri

**flowId**

required

string

\>= 1 characters

**flowVersion**

required

string

\>= 1 characters

**projectId**

required

The trigger’s project: its runs are this project’s.

string format: uuid

**owner**

required

Who its runs act as. A person: a personal trigger, which only they or a project admin change. A service account: a shared trigger. Only a take-over (`POST …/owner`) changes it. Checked again at every delivery.

object

**kind**

required

string

Allowed values: user service

**id**

required

string

**displayName**

The owner’s name at the time of the response: the person’s display name, or the service account’s name. Absent when it can’t be read (no directory, a removed account) and from a runtime before Kindgi 0.1.6: show the id then.

string

**isProjectAccount**

Set when the owner is the trigger’s project’s own service account (`runAsProject`): the trigger is shared, and runs as the project. Show it as “This project”. Absent otherwise.

boolean

**input**

The flow’s input on every delivery, in place of the event. Absent: the event (the body, parsed as JSON for a JSON content type, else its text).

**hmacSecretName**

required

The signing secret, by name (written with `POST /v1/secrets`, or made by `generateSecret`). Read in the env the deployment serves, at `hmacSecretScope`.

string

\>= 1 characters

**hmacSecretScope**

One of:

- **object**

  One of:

  - **object**

    object

    **kind**

    required

    string

    Allowed value: tenant

  - **object**

    object

    **kind**

    required

    string

    Allowed value: org

    **orgId**

    required

    string format: uuid

  - **object**

    object

    **kind**

    required

    string

    Allowed value: project

    **projectId**

    required

    string format: uuid

- **null**

  null

**signingSecret**

Only in the answer to a register with `generateSecret`: the new signing secret, shown this once, to paste into the sender. It’s stored as `hmacSecretName` in the trigger’s own scope; no later read shows it.

string

**signature**

required

One of:

- **object**

  object

  **kind**

  required

  string

  Allowed values: hmac-sha256

  **encoding**

  required

  string

  Allowed values: hex base64

  **header**

  required

  The header that carries the signature (matched case-insensitively).

  string

  \>= 1 characters <= 100 characters

  **prefix**

  Stripped from the header’s value before decoding (e.g. `sha256=`).

  string

  \>= 1 characters <= 32 characters

- **object**

  object

  **kind**

  required

  string

  Allowed values: standard-webhooks

  **toleranceSeconds**

  integer

  \>= 1 <= 3600

**deliveryIdHeader**

The header whose value, with the body, is a delivery’s dedupe key (WooCommerce: `X-WC-Webhook-Delivery-ID`, which is per second, not per event, so the body counts too). Absent: deliveries aren’t deduped (`standard-webhooks` dedupes on its own `webhook-id`).

string

**bodyLimitBytes**

required

The largest body it takes (default 262144).

integer

\>= 1024 <= 1048576

**rateLimitPerMinute**

required

Accepted deliveries a minute (default 600); past it, `429`.

integer

\>= 1 <= 6000

**label**

required

string | null

**status**

required

Lifecycle status. Only `active` triggers fire. Tombstoned rows are excluded from every read path.

string

Allowed values: active paused

**statusReason**

Why the runtime paused it (repeated refused or failed starts; a sender’s failed proofs never count), when it did.

string

**version**

The trigger’s newest version (`GET …/versions`): one more for each change to what it runs, when, as whom or with what. Absent until its first version.

integer

\>= 1

**suppressedRefusals**

Refusals and skipped deliveries this minute past the 20 recorded in its history, which only counted.

object

**since**

required

string format: date-time

**count**

required

integer

\>= 1

**toolsAllowedUnattended**

The tools whose calls in this trigger’s runs don’t wait for a person’s yes, though a tool asks for one in a run no person started (`needsApprovalWhenUnattended`). Absent: none. Each tool by its id, with no wildcard or pattern, and only tools this trigger’s flow or agent calls. It lifts only that wait: the tenant’s approval rules, the agent’s own rules, the permission check (as the trigger’s owner) and the tool’s own limits all still apply.

Array\<string>

<= 100 items unique items

**lastFiredAt**

required

string | null format: date-time

**createdAt**

required

string format: date-time

**updatedAt**

required

string format: date-time

##### Example

```json
{
  "owner": {
    "kind": "user",
    "isProjectAccount": true
  },
  "hmacSecretScope": {
    "kind": "tenant"
  },
  "signature": {
    "kind": "hmac-sha256",
    "encoding": "hex"
  },
  "status": "active"
}
```

### 400

A body that fails validation (`bad-input`), or `provider-key-refused`: `hmacSecretName` is a model provider’s key, or `reserved-name-prefix`: it’s under the reserved `kindgi.` prefix. Or `reference-out-of-scope`: the new `flowVersion` isn’t a version the trigger’s project runs, such as one made in an earlier environment and not yet promoted (`details.issues`, at `/flowVersion`, whose message names the version). Nothing changes.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 401

Missing / malformed / expired / revoked bearer token.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 403

`permission-denied`: the trigger is personal and the caller is neither its owner nor a project admin (the message names the owner), or it’s shared and changing what it runs takes a project admin. A caller without `write` on its project is refused first, and learns nothing of its owner.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 404

No webhook trigger with that id.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 409

Idempotency-Key was reused with a different body, or resource-state conflict.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 413

The request body is over the API’s limit (`request-too-large`; `details.maxBytes` says how much).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 500

Server error (unmapped domain code or framework crash).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 503

The `Idempotency-Key` store couldn’t be read before the request ran (`idempotency-store-unavailable`), so nothing was changed; `Retry-After` (whole seconds) says when to try again with the same key.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

## See also

- [`GET /v1/webhooks/{triggerId}`: Fetch a webhook trigger](https://docs.kindgi.com/v0.3.1/reference/api/operations/webhooksget.md)
- [`POST /v1/webhooks/{triggerId}/pause`: Pause a webhook trigger](https://docs.kindgi.com/v0.3.1/reference/api/operations/webhookspause.md)
- [`POST /v1/webhooks/{triggerId}/resume`: Resume a webhook trigger](https://docs.kindgi.com/v0.3.1/reference/api/operations/webhooksresume.md)
- [`POST /v1/webhooks/{triggerId}/unregister`: Soft-delete a webhook trigger (tombstone)](https://docs.kindgi.com/v0.3.1/reference/api/operations/webhooksunregister.md)
- [Error code `bad-input`](https://docs.kindgi.com/v0.3.1/reference/errors.md#bad-input)
- [Error code `idempotency-store-unavailable`](https://docs.kindgi.com/v0.3.1/reference/errors.md#idempotency-store-unavailable)
- [Error code `permission-denied`](https://docs.kindgi.com/v0.3.1/reference/errors.md#permission-denied)
- [Error code `provider-key-refused`](https://docs.kindgi.com/v0.3.1/reference/errors.md#provider-key-refused)
- [Error code `reference-out-of-scope`](https://docs.kindgi.com/v0.3.1/reference/errors.md#reference-out-of-scope)
- [Error code `request-too-large`](https://docs.kindgi.com/v0.3.1/reference/errors.md#request-too-large)
- [Error code `reserved-name-prefix`](https://docs.kindgi.com/v0.3.1/reference/errors.md#reserved-name-prefix)
- [Every error code](https://docs.kindgi.com/v0.3.1/reference/errors.md)
