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

# Webhooks

> Subscribe to Cloud Agents lifecycle events over HTTP webhooks: endpoint API, delivery and signature verification, and the full event catalog.

## Overview

Webhooks are an event-driven push mechanism provided by Qoder Cloud Agents. When resources such as Agents or Sessions undergo lifecycle changes, the system delivers structured events via HTTP POST to developer-registered URLs — no polling required.

**Core Features:**

* **Event-driven push** — Proactive notifications on resource state changes, no client polling needed
* **Envelope structure** — Uniform `BetaWebhookEvent { id, created_at, data, type:"event" }` format
* **Delivery semantics** — At-least-once guarantee; HMAC-SHA256 signature verification; exponential backoff retry
* **Wildcard subscription** — Use `*` to subscribe to all event types, simplifying integration

**Use Cases:**

| Scenario                           | Description                                            |
| ---------------------------------- | ------------------------------------------------------ |
| Async task completion notification | Trigger downstream workflows when a Session finishes   |
| Agent configuration audit          | Track Agent create/update/delete operations            |
| Multi-Agent orchestration          | Drive sub-task scheduling on Thread state changes      |
| Ops monitoring & alerting          | Alert when consecutive failure count exceeds threshold |

***

## Domain Types

This section defines the data structures of Webhook events. The `data` field of each event type follows a uniform object format containing the resource ID, event type, and event-specific additional fields.

***

### Session Lifecycle Events

***

### Webhook Session Created Event Data

* `WebhookSessionCreatedEventData object { id, type }`

  * `id: string`

    The Session ID that triggered the event.

  * `type: "session.created"`

    * `"session.created"`

    Triggered when a Session is successfully created. The system sends this event immediately after a Session is created via `POST /sessions`.

***

### Webhook Session Updated Event Data

* `WebhookSessionUpdatedEventData object { id, type }`

  * `id: string`

    The Session ID that triggered the event.

  * `type: "session.updated"`

    * `"session.updated"`

    Triggered when Session metadata (such as title, metadata, etc.) is modified.

***

### Webhook Session Archived Event Data

* `WebhookSessionArchivedEventData object { id, type }`

  * `id: string`

    The Session ID that triggered the event.

  * `type: "session.archived"`

    * `"session.archived"`

    Triggered when a Session is archived. After archival, the Session no longer accepts new messages, but historical data remains queryable.

***

### Webhook Session Deleted Event Data

* `WebhookSessionDeletedEventData object { id, type }`

  * `id: string`

    The Session ID that triggered the event.

  * `type: "session.deleted"`

    * `"session.deleted"`

    Triggered when a Session is permanently deleted. All data for the Session becomes unrecoverable after deletion.

***

### Session Status Events

***

### Webhook Session Status Run Started Event Data

* `WebhookSessionStatusRunStartedEventData object { id, type }`

  * `id: string`

    The Session ID that triggered the event.

  * `type: "session.status_run_started"`

    * `"session.status_run_started"`

    Triggered when an Agent starts a run. Signals that the Session has entered the running state and is executing user instructions.

***

### Webhook Session Status Idled Event Data

* `WebhookSessionStatusIdledEventData object { id, type }`

  * `id: string`

    The Session ID that triggered the event.

  * `type: "session.status_idled"`

    * `"session.status_idled"`

    Triggered when a turn completes and the Session returns to idle state. At this point it is safe to read the Session's latest output.

***

### Session Thread Events

Applicable to multi-Agent collaboration scenarios. Thread events carry an additional `session_thread_id` field on top of the base fields, identifying the specific execution thread.

***

### Webhook Session Thread Created Event Data

* `WebhookSessionThreadCreatedEventData object { id, type, session_thread_id }`

  * `id: string`

    The Session ID that triggered the event.

  * `type: "session.thread_created"`

    * `"session.thread_created"`

    Triggered when a new Session Thread is created. Common in sub-Agent collaboration scenarios where the main Agent spawns child threads to execute tasks.

  * `session_thread_id: string`

    The associated Session Thread ID.

***

### Webhook Session Thread Idled Event Data

* `WebhookSessionThreadIdledEventData object { id, type, session_thread_id }`

  * `id: string`

    The Session ID that triggered the event.

  * `type: "session.thread_idled"`

    * `"session.thread_idled"`

    Triggered when a Session Thread finishes a run and enters idle state. Indicates the thread has completed its current task and results can be read.

  * `session_thread_id: string`

    The associated Session Thread ID.

***

### Webhook Session Thread Terminated Event Data

* `WebhookSessionThreadTerminatedEventData object { id, type, session_thread_id }`

  * `id: string`

    The Session ID that triggered the event.

  * `type: "session.thread_terminated"`

    * `"session.thread_terminated"`

    Triggered when a Session Thread is terminated. A terminated Thread cannot be recovered; a new Thread must be created to continue work.

  * `session_thread_id: string`

    The associated Session Thread ID.

***

### Agent Lifecycle Events

Agent events carry an additional `version` field beyond the base fields, indicating the Agent's configuration version number.

***

### Webhook Agent Created Event Data

* `WebhookAgentCreatedEventData object { id, type, version }`

  * `id: string`

    The Agent ID that triggered the event.

  * `type: "agent.created"`

    * `"agent.created"`

    Triggered when an Agent is successfully created. The system sends this event after an Agent is created via `POST /agents`.

  * `version: integer`

    Agent version number. Set to `1` on initial creation.

***

### Webhook Agent Updated Event Data

* `WebhookAgentUpdatedEventData object { id, type, version }`

  * `id: string`

    The Agent ID that triggered the event.

  * `type: "agent.updated"`

    * `"agent.updated"`

    Triggered when an Agent's configuration is updated. The `version` increments with each update.

  * `version: integer`

    Agent version number.

***

### Webhook Agent Archived Event Data

* `WebhookAgentArchivedEventData object { id, type, version }`

  * `id: string`

    The Agent ID that triggered the event.

  * `type: "agent.archived"`

    * `"agent.archived"`

    Triggered when an Agent is archived. After archival, the Agent no longer accepts new Session creation requests.

  * `version: integer`

    Agent version number.

***

### Webhook Agent Deleted Event Data

* `WebhookAgentDeletedEventData object { id, type, version }`

  * `id: string`

    The Agent ID that triggered the event.

  * `type: "agent.deleted"`

    * `"agent.deleted"`

    Triggered when an Agent is deleted. All configuration data for the Agent becomes unrecoverable after deletion.

  * `version: integer`

    Agent version number.

***

## Webhook Endpoint API

CRUD endpoints for managing Webhook endpoints. Use these to create, query, update, and delete Webhook endpoints, as well as send test events and control endpoint enable/disable state.

**Base URL:**

| Region | Address                                 |
| ------ | --------------------------------------- |
| Global | `https://api.qoder.com/api/v1/cloud`    |
| CN     | `https://api.qoder.com.cn/api/v1/cloud` |

**Authentication:**

All endpoints require a Personal Access Token in the request header:

```
Authorization: Bearer $PAT
```

***

### POST /webhook\_endpoints

Create a new Webhook endpoint.

**Request Parameters:**

| Field         | Type      | Required | Description                                                                               |
| ------------- | --------- | -------- | ----------------------------------------------------------------------------------------- |
| `url`         | string    | Yes      | HTTP or HTTPS URL to receive event deliveries (HTTPS strongly recommended for production) |
| `description` | string    | No       | Endpoint description for management purposes                                              |
| `events`      | string\[] | Yes      | List of subscribed event types, supports `*` wildcard                                     |

**Response:** `201 Created`

```json theme={null}
{
  "id": "5fc05310-4d9c-447e-a4b8-f124f17e1ff0",
  "url": "https://webhook.site/your-unique-path",
  "description": "quickstart demo",
  "events": ["*"],
  "active": true,
  "signing_secret": "whsec_BfJDodFEzmkdjAUf19-_XthSq6KbPKmRjfm9KFWMIuk",
  "signing_secret_version": 1,
  "created_at": "2026-07-02T18:17:30.069191+08:00"
}
```

> **Note:** `signing_secret` is only returned once at creation time. Store it securely. Subsequent query endpoints will not return this field.

**Status Codes:**

| Code | Description                                                          |
| ---- | -------------------------------------------------------------------- |
| 201  | Created successfully                                                 |
| 400  | Invalid request parameters (e.g., malformed URL, invalid event type) |
| 401  | Unauthorized, invalid or expired token                               |
| 422  | Semantic error (e.g., duplicate URL registration)                    |

**Example:**

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints \
  -H "Authorization: Bearer $PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://webhook.site/your-unique-path",
    "description": "quickstart demo",
    "events": ["*"]
  }'
```

***

### GET /webhook\_endpoints

List all Webhook endpoints under the current account.

**Request Parameters:** None

**Response:** `200 OK`

```json theme={null}
{
  "data": [
    {
      "id": "700e7789-edab-41cb-b60a-210d0df38ab6",
      "url": "https://myapp.example.com/hooks/qoder",
      "description": "prod app hook",
      "events": ["session.created", "session.thread_idled"],
      "active": true,
      "signing_secret_version": 1,
      "consecutive_fail": 0,
      "last_success_at": "2026-07-02T18:08:12.666783+08:00",
      "created_at": "2026-07-02T17:47:16.073822+08:00",
      "updated_at": "2026-07-02T17:47:16.073822+08:00"
    }
  ]
}
```

**Response Fields:**

| Field                    | Type      | Description                                                |
| ------------------------ | --------- | ---------------------------------------------------------- |
| `id`                     | string    | Endpoint unique identifier                                 |
| `url`                    | string    | URL receiving events                                       |
| `description`            | string    | Endpoint description                                       |
| `events`                 | string\[] | Subscribed event types                                     |
| `active`                 | boolean   | Whether enabled                                            |
| `signing_secret_version` | integer   | Signing secret version number                              |
| `consecutive_fail`       | integer   | Consecutive delivery failure count                         |
| `last_success_at`        | string    | Last successful delivery time (RFC 3339)                   |
| `last_failure_at`        | string    | Last delivery failure time (RFC 3339), null if no failures |
| `created_at`             | string    | Creation time (RFC 3339)                                   |
| `updated_at`             | string    | Last update time (RFC 3339)                                |

**Example:**

```bash theme={null}
curl https://api.qoder.com/api/v1/cloud/webhook_endpoints \
  -H "Authorization: Bearer $PAT"
```

***

### GET /webhook\_endpoints/\{id}

Retrieve details of a specific Webhook endpoint.

**Path Parameters:**

| Parameter | Type   | Description         |
| --------- | ------ | ------------------- |
| `id`      | string | Webhook endpoint ID |

**Response:** `200 OK`

Returns a single endpoint object with the same structure as elements in the list endpoint.

**Status Codes:**

| Code | Description        |
| ---- | ------------------ |
| 200  | Success            |
| 404  | Endpoint not found |

**Example:**

```bash theme={null}
curl https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6 \
  -H "Authorization: Bearer $PAT"
```

***

### PUT /webhook\_endpoints/\{id}

Update the configuration of a specific Webhook endpoint.

**Path Parameters:**

| Parameter | Type   | Description         |
| --------- | ------ | ------------------- |
| `id`      | string | Webhook endpoint ID |

**Request Parameters:**

| Field         | Type      | Required | Description                   |
| ------------- | --------- | -------- | ----------------------------- |
| `url`         | string    | No       | Update the event delivery URL |
| `description` | string    | No       | Update endpoint description   |
| `events`      | string\[] | No       | Update subscribed event types |

**Response:** `200 OK`

Returns the full updated endpoint object.

**Status Codes:**

| Code | Description                |
| ---- | -------------------------- |
| 200  | Updated successfully       |
| 400  | Invalid request parameters |
| 404  | Endpoint not found         |

**Example:**

```bash theme={null}
curl -X PUT https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6 \
  -H "Authorization: Bearer $PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["session.created", "session.status_idled", "agent.updated"],
    "description": "updated prod hook"
  }'
```

***

### DELETE /webhook\_endpoints/\{id}

Permanently delete a Webhook endpoint. All undelivered events will be discarded.

**Path Parameters:**

| Parameter | Type   | Description         |
| --------- | ------ | ------------------- |
| `id`      | string | Webhook endpoint ID |

**Response:** `204 No Content`

**Status Codes:**

| Code | Description          |
| ---- | -------------------- |
| 204  | Deleted successfully |
| 404  | Endpoint not found   |

**Example:**

```bash theme={null}
curl -X DELETE https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6 \
  -H "Authorization: Bearer $PAT"
```

***

### POST /webhook\_endpoints/\{id}/test

Send a test event to the specified endpoint to verify connectivity and signature verification logic.

**Path Parameters:**

| Parameter | Type   | Description         |
| --------- | ------ | ------------------- |
| `id`      | string | Webhook endpoint ID |

**Request Parameters:** None

**Response:** `202 Accepted`

```json theme={null}
{"event_id": 433, "delivery_rows": 2}
```

**Response Fields:**

| Field           | Type    | Description                                             |
| --------------- | ------- | ------------------------------------------------------- |
| `event_id`      | integer | Internal ID of the test event                           |
| `delivery_rows` | integer | Number of message partitions the event was published to |

**Status Codes:**

| Code | Description        |
| ---- | ------------------ |
| 202  | Test event sent    |
| 404  | Endpoint not found |

**Example:**

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6/test \
  -H "Authorization: Bearer $PAT"
```

***

### POST /webhook\_endpoints/\{id}/enable

Enable a disabled Webhook endpoint. Once enabled, the endpoint will resume receiving event deliveries.

**Path Parameters:**

| Parameter | Type   | Description         |
| --------- | ------ | ------------------- |
| `id`      | string | Webhook endpoint ID |

**Request Parameters:** None

**Response:** `200 OK`

Returns the endpoint object after enabling (`active: true`).

**Status Codes:**

| Code | Description          |
| ---- | -------------------- |
| 200  | Enabled successfully |
| 404  | Endpoint not found   |

**Example:**

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6/enable \
  -H "Authorization: Bearer $PAT"
```

***

### POST /webhook\_endpoints/\{id}/disable

Disable a Webhook endpoint. Once disabled, the endpoint will stop receiving event deliveries but will not be deleted.

**Path Parameters:**

| Parameter | Type   | Description         |
| --------- | ------ | ------------------- |
| `id`      | string | Webhook endpoint ID |

**Request Parameters:** None

**Response:** `200 OK`

Returns the endpoint object after disabling (`active: false`).

**Status Codes:**

| Code | Description           |
| ---- | --------------------- |
| 200  | Disabled successfully |
| 404  | Endpoint not found    |

**Example:**

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints/700e7789-edab-41cb-b60a-210d0df38ab6/disable \
  -H "Authorization: Bearer $PAT"
```

***

### GET /webhook\_events

List Webhook event delivery records for auditing and troubleshooting.

**Request Parameters:** None

**Response:** `200 OK`

Returns an event list containing delivery status, timestamps, and other information.

**Example:**

```bash theme={null}
curl https://api.qoder.com/api/v1/cloud/webhook_events \
  -H "Authorization: Bearer $PAT"
```

***

### GET /webhook\_events/\{id}

Retrieve detailed information for a single Webhook event.

**Path Parameters:**

| Parameter | Type   | Description      |
| --------- | ------ | ---------------- |
| `id`      | string | Webhook event ID |

**Response:** `200 OK`

**Status Codes:**

| Code | Description     |
| ---- | --------------- |
| 200  | Success         |
| 404  | Event not found |

**Example:**

```bash theme={null}
curl https://api.qoder.com/api/v1/cloud/webhook_events/433 \
  -H "Authorization: Bearer $PAT"
```

***

### Error Response Format

All endpoints return a uniform error structure when an error occurs:

```json theme={null}
{
  "error": {
    "message": "Unknown event type: agent.updated",
    "type": "invalid_request_error"
  },
  "request_id": "cc865161-b25d-4d68-bad5-bf0363f6e0f9",
  "type": "error"
}
```

**Error Fields:**

| Field           | Type   | Description                                                       |
| --------------- | ------ | ----------------------------------------------------------------- |
| `error.message` | string | Human-readable error description                                  |
| `error.type`    | string | Error category (e.g., `invalid_request_error`, `not_found_error`) |
| `request_id`    | string | Request trace ID for troubleshooting with support team            |
| `type`          | string | Fixed value `"error"`                                             |

> **Note:** 401 responses from the authentication layer (e.g., invalid or missing Token) may not follow the business error structure above, as they are returned directly by the gateway.

***

## Webhook Delivery

### Delivery Method

The system delivers events to registered URLs via HTTP POST with the following format:

* **Method:** `POST`
* **Content-Type:** `application/json`
* **Body:** JSON envelope structure (see [Envelope Structure](#envelope-structure))

### Request Headers

Each delivery includes the following HTTP headers:

| Header              | Description                                                           |
| ------------------- | --------------------------------------------------------------------- |
| `Content-Type`      | `application/json`                                                    |
| `User-Agent`        | `QoderCloudAgents-Webhook/1.0`                                        |
| `Webhook-Signature` | HMAC-SHA256 signature in format `t=<unix_epoch>,v1=<hmac_sha256_hex>` |

### Signature Verification

To ensure event authenticity and data integrity, every delivery carries an HMAC-SHA256 signature. Developers should verify the signature before processing events.

**Signature Format:**

```
t=1719907336,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8f9
```

**Signature Algorithm:**

```
signature = HMAC_SHA256(signing_secret, "<t>.<raw_body>")
```

Where:

* `signing_secret` — The secret returned when creating the endpoint (`whsec_` prefix)
* `t` — Unix timestamp at signing time (seconds)
* `raw_body` — Raw byte content of the request body (unparsed)

**Verification Steps:**

1. Extract `t` and `v1` from the `Webhook-Signature` header
2. Check that the timestamp is within the tolerance window (recommended 600 seconds) to prevent replay attacks
3. Compute HMAC-SHA256 of `"<t>.<raw_body>"` using the `signing_secret`
4. Compare the result with `v1` using timing-safe comparison

### Signature Verification Code Examples

**Node.js:**

```js theme={null}
const crypto = require('node:crypto');

function verifyWebhook(secret, rawBody, header, toleranceSec = 600) {
  const parts = Object.fromEntries(
    header.split(',').map(kv => kv.split('=').map(s => s.trim()))
  );
  const ts = parseInt(parts.t, 10);
  if (Math.abs(Date.now() / 1000 - ts) > toleranceSec) return false;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${rawBody}`)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(parts.v1, 'hex')
  );
}
```

**Python:**

```python theme={null}
import hmac, hashlib, time

def verify_webhook(secret: bytes, raw_body: bytes, header: str, tolerance_sec: int = 600) -> bool:
    parts = dict(kv.split("=", 1) for kv in header.split(","))
    ts = int(parts["t"])
    if abs(time.time() - ts) > tolerance_sec:
        return False
    signed_payload = f"{ts}.".encode() + raw_body
    expected = hmac.new(secret, signed_payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])
```

**Go:**

```go theme={null}
func verifyWebhook(secret, rawBody []byte, header string, toleranceSec int64) bool {
    var ts int64; var v1 string
    for _, kv := range strings.Split(header, ",") {
        kv = strings.TrimSpace(kv)
        if strings.HasPrefix(kv, "t=") { ts, _ = strconv.ParseInt(kv[2:], 10, 64) }
        if strings.HasPrefix(kv, "v1=") { v1 = kv[3:] }
    }
    if ts == 0 || v1 == "" { return false }
    if diff := time.Now().Unix() - ts; diff > toleranceSec || diff < -toleranceSec { return false }
    mac := hmac.New(sha256.New, secret)
    fmt.Fprintf(mac, "%d.", ts)
    mac.Write(rawBody)
    return hmac.Equal([]byte(hex.EncodeToString(mac.Sum(nil))), []byte(v1))
}
```

### Retry Strategy

When delivery fails, the system uses exponential backoff for retries:

| Attempt | Delay      | Description      |
| ------- | ---------- | ---------------- |
| 1st     | Immediate  | Initial delivery |
| 2nd     | 1 second   | First retry      |
| 3rd     | 5 seconds  | Second retry     |
| 4th     | 30 seconds | Final retry      |

A total of 4 attempts (1 delivery + 3 retries). After all failures, the event enters the dead letter queue.

### Response Code Handling

| Response Code Range | Handling                                                                         |
| ------------------- | -------------------------------------------------------------------------------- |
| 2xx                 | Delivery successful, event marked as delivered                                   |
| 4xx                 | No retry, event marked as discarded (client errors should be fixed by developer) |
| 5xx                 | Triggers retry (temporary server failure)                                        |
| Timeout             | Triggers retry (default timeout 30 seconds)                                      |

### Automatic Degradation

When an endpoint's `consecutive_fail` count exceeds **20**, the system triggers a degradation warning. Developers should monitor this metric and check the following when failures persist:

* Is the endpoint URL reachable
* Is the SSL certificate valid
* Is the server responding normally
* Are there bugs in signature verification logic

***

## Supported Event Types (Complete List)

### Event Type Overview

| Event Type                                | Status         | Additional data fields | Trigger Condition                           |
| ----------------------------------------- | -------------- | ---------------------- | ------------------------------------------- |
| `session.created`                         | ✅ Implemented  | —                      | POST /sessions successfully creates Session |
| `session.updated`                         | ✅ Implemented  | —                      | Session metadata modified                   |
| `session.archived`                        | ✅ Implemented  | —                      | Session archived                            |
| `session.deleted`                         | ✅ Implemented  | —                      | Session permanently deleted                 |
| `session.status_run_started`              | ✅ Implemented  | —                      | Agent starts a run                          |
| `session.status_idled`                    | ✅ Implemented  | —                      | Turn completed, returned to idle            |
| `session.thread_created`                  | ✅ Implemented  | `session_thread_id`    | New Session Thread created                  |
| `session.thread_idled`                    | ✅ Implemented  | `session_thread_id`    | Thread execution finished, entered idle     |
| `session.thread_terminated`               | ✅ Implemented  | `session_thread_id`    | Thread terminated                           |
| `agent.created`                           | ✅ Implemented  | `version`              | POST /agents creates Agent                  |
| `agent.updated`                           | ✅ Implemented  | `version`              | Agent configuration updated                 |
| `agent.archived`                          | ✅ Implemented  | `version`              | Agent archived                              |
| `agent.deleted`                           | ✅ Implemented  | `version`              | Agent deleted                               |
| `session.pending`                         | 🔜 Allowlisted | —                      | Session awaiting scheduling                 |
| `session.running`                         | 🔜 Allowlisted | —                      | Session running                             |
| `session.idled`                           | 🔜 Allowlisted | —                      | Session entered idle                        |
| `session.requires_action`                 | 🔜 Allowlisted | —                      | Session requires human intervention         |
| `session.status_run_failed`               | 🔜 Allowlisted | —                      | Run failed                                  |
| `session.status_terminated`               | 🔜 Allowlisted | —                      | Session terminated                          |
| `session.status_rescheduled`              | 🔜 Allowlisted | —                      | Session rescheduled                         |
| `session.status_paused_pending_input`     | 🔜 Allowlisted | —                      | Paused awaiting user input                  |
| `session.status_paused_pending_approval`  | 🔜 Allowlisted | —                      | Paused awaiting approval                    |
| `session.status_paused_user_intervention` | 🔜 Allowlisted | —                      | Paused awaiting human intervention          |
| `session.outcome_evaluation_started`      | 🔜 Allowlisted | —                      | Outcome evaluation started                  |
| `session.outcome_evaluation_ended`        | 🔜 Allowlisted | —                      | Outcome evaluation ended                    |
| `vault.created`                           | 🔜 Allowlisted | —                      | Vault created                               |
| `vault.deleted`                           | 🔜 Allowlisted | —                      | Vault deleted                               |
| `vault.updated`                           | 🔜 Allowlisted | —                      | Vault updated                               |
| `vault_credential.created`                | 🔜 Allowlisted | —                      | Credential created                          |
| `vault_credential.updated`                | 🔜 Allowlisted | —                      | Credential updated                          |
| `vault_credential.deleted`                | 🔜 Allowlisted | —                      | Credential deleted                          |
| `vault_credential.shared`                 | 🔜 Allowlisted | —                      | Credential shared                           |
| `vault_credential.revoked`                | 🔜 Allowlisted | —                      | Credential revoked                          |
| `deployment.created`                      | 📋 Planned     | —                      | Deployment created                          |
| `deployment.updated`                      | 📋 Planned     | —                      | Deployment updated                          |
| `deployment.archived`                     | 📋 Planned     | —                      | Deployment archived                         |
| `deployment.deleted`                      | 📋 Planned     | —                      | Deployment deleted                          |
| `deployment.paused`                       | 📋 Planned     | —                      | Deployment paused                           |
| `deployment.unpaused`                     | 📋 Planned     | —                      | Deployment unpaused                         |
| `deployment_run.started`                  | 📋 Planned     | —                      | Deployment run started                      |
| `deployment_run.failed`                   | 📋 Planned     | —                      | Deployment run failed                       |
| `deployment_run.succeeded`                | 📋 Planned     | —                      | Deployment run succeeded                    |
| `environment.created`                     | 📋 Planned     | —                      | Environment created                         |
| `environment.updated`                     | 📋 Planned     | —                      | Environment updated                         |
| `environment.archived`                    | 📋 Planned     | —                      | Environment archived                        |
| `environment.deleted`                     | 📋 Planned     | —                      | Environment deleted                         |
| `memory_store.created`                    | 📋 Planned     | —                      | Memory store created                        |
| `memory_store.archived`                   | 📋 Planned     | —                      | Memory store archived                       |
| `memory_store.deleted`                    | 📋 Planned     | —                      | Memory store deleted                        |

### Special Event Types

| Event Type     | Description                                                                                           |
| -------------- | ----------------------------------------------------------------------------------------------------- |
| `*`            | Wildcard — subscribes to all event types. Use `["*"]` when creating an endpoint to receive all events |
| `webhook.test` | Internal test event, triggered via `POST /webhook_endpoints/{id}/test`                                |

### Status Labels

| Label          | Meaning                                                                                               |
| -------------- | ----------------------------------------------------------------------------------------------------- |
| ✅ Implemented  | Complete event trigger points exist in the system; can be subscribed to and received normally         |
| 🔜 Allowlisted | Event type registered in the validation allowlist (usable in `events` field) but no trigger point yet |
| 📋 Planned     | Event type defined at the API design level but not yet registered in the allowlist                    |

***

## Envelope Structure

All Webhook events are wrapped in a uniform envelope structure for delivery.

### Base Envelope Structure

```json theme={null}
{
  "id": "whevt_a1b2c3d4e5f67890",
  "created_at": "2026-07-02T10:02:16Z",
  "type": "event",
  "data": {
    "id": "sess_019f224773fe71d79c5869bd089d159c",
    "type": "session.created"
  }
}
```

### Thread Event Envelope

Thread events include an additional `session_thread_id` field in `data`:

```json theme={null}
{
  "id": "whevt_a1b2c3d4e5f67890",
  "created_at": "2026-07-02T10:02:17Z",
  "type": "event",
  "data": {
    "id": "sess_019f224773fe71d79c5869bd089d159c",
    "type": "session.thread_idled",
    "session_thread_id": "sthread_019f224..."
  }
}
```

### Agent Event Envelope

Agent events include an additional `version` field in `data`:

```json theme={null}
{
  "id": "whevt_a1b2c3d4e5f67890",
  "created_at": "2026-07-02T10:02:16Z",
  "type": "event",
  "data": {
    "id": "agent_019f224773fe71d7...",
    "type": "agent.created",
    "version": 1
  }
}
```

### Field Descriptions

| Field                    | Type    | Description                                                                                                                 |
| ------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------- |
| `id`                     | string  | Event unique identifier. Format: `whevt_` + unique identifier, deterministically generated from the event's idempotency key |
| `created_at`             | string  | Event creation time, RFC 3339 UTC format                                                                                    |
| `type`                   | string  | Fixed value `"event"`, identifies this as an event envelope                                                                 |
| `data`                   | object  | Event payload containing trigger resource information                                                                       |
| `data.id`                | string  | ID of the resource that triggered the event (Session ID or Agent ID)                                                        |
| `data.type`              | string  | Event type string, e.g., `"session.created"`, `"agent.updated"`                                                             |
| `data.session_thread_id` | string  | (Thread events only) Associated Session Thread ID                                                                           |
| `data.version`           | integer | (Agent events only) Agent configuration version number                                                                      |

### Idempotency Handling

The `id` field in the envelope can be used as a deduplication key. Since delivery semantics are at-least-once, the same event may be delivered multiple times. Receivers should:

1. Use `id` as a unique key for deduplication
2. Check whether the `id` has already been consumed before processing
3. Ensure event processing logic is idempotent

***

## Planned Events (Coming Soon)

The following event types have been aligned at the API design level and will be rolled out as their respective resource features go live. Specific timelines depend on product requirements.

### deployment.\* (Deployment Lifecycle, 6 events)

| Event Type            | Trigger Condition                             |
| --------------------- | --------------------------------------------- |
| `deployment.created`  | New deployment created                        |
| `deployment.updated`  | Deployment configuration updated              |
| `deployment.archived` | Deployment archived                           |
| `deployment.deleted`  | Deployment deleted                            |
| `deployment.paused`   | Deployment paused (stops triggering new runs) |
| `deployment.unpaused` | Deployment resumed (resumes triggering runs)  |

### deployment\_run.\* (Deployment Run Status, 3 events)

| Event Type                 | Trigger Condition                     |
| -------------------------- | ------------------------------------- |
| `deployment_run.started`   | A deployment run starts execution     |
| `deployment_run.failed`    | Deployment run failed                 |
| `deployment_run.succeeded` | Deployment run completed successfully |

### environment.\* (Environment Management, 4 events)

| Event Type             | Trigger Condition                 |
| ---------------------- | --------------------------------- |
| `environment.created`  | Runtime environment created       |
| `environment.updated`  | Environment configuration updated |
| `environment.archived` | Environment archived              |
| `environment.deleted`  | Environment deleted               |

### memory\_store.\* (Memory Store, 3 events)

| Event Type              | Trigger Condition             |
| ----------------------- | ----------------------------- |
| `memory_store.created`  | Memory store instance created |
| `memory_store.archived` | Memory store archived         |
| `memory_store.deleted`  | Memory store deleted          |

### vault.\* (Vault, 3 events)

| Event Type      | Trigger Condition           |
| --------------- | --------------------------- |
| `vault.created` | Vault created               |
| `vault.updated` | Vault configuration updated |
| `vault.deleted` | Vault deleted               |

### vault\_credential.\* (Credential Management, 5 events)

| Event Type                 | Trigger Condition                      |
| -------------------------- | -------------------------------------- |
| `vault_credential.created` | New credential added to vault          |
| `vault_credential.updated` | Credential information updated         |
| `vault_credential.deleted` | Credential deleted                     |
| `vault_credential.shared`  | Credential shared with other Agents    |
| `vault_credential.revoked` | Credential sharing permissions revoked |

***

## Appendix A: Quick Start Guide

### Step 1: Create a Webhook Endpoint

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints \
  -H "Authorization: Bearer $PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/webhooks/qoder",
    "description": "Production webhook",
    "events": ["session.status_idled", "session.thread_idled"]
  }'
```

### Step 2: Save the signing\_secret

Retrieve the `signing_secret` from the creation response and store it securely for later signature verification.

### Step 3: Implement the Receiver

Implement the Webhook receiver endpoint in your service, ensuring:

1. Verify the `Webhook-Signature` signature
2. Use the event `id` for idempotent deduplication
3. Return `200 OK` to acknowledge receipt
4. Process business logic asynchronously (avoid timeouts)

### Step 4: Send a Test Event

```bash theme={null}
curl -X POST https://api.qoder.com/api/v1/cloud/webhook_endpoints/{id}/test \
  -H "Authorization: Bearer $PAT"
```

### Step 5: Verify and Go Live

After confirming test events are received correctly, you're ready for production use.

***

## Appendix B: Best Practices

| Practice               | Description                                                                          |
| ---------------------- | ------------------------------------------------------------------------------------ |
| Signature verification | Always verify `Webhook-Signature`, reject invalid requests                           |
| Idempotent processing  | Use event `id` for deduplication to prevent duplicate consumption                    |
| Fast response          | Receiver should return 2xx within 5 seconds; run time-consuming logic asynchronously |
| Timestamp validation   | Reject events outside the tolerance window to prevent replay attacks                 |
| Precise subscription   | Only subscribe to needed event types to reduce unnecessary network overhead          |
| Monitoring & alerting  | Monitor `consecutive_fail` metric to detect delivery anomalies promptly              |
