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

# LangSmith Engine webhook events

> Reference for the webhook events the LangSmith Engine sends when it creates issues or links new traces to existing issues.

Forward LangSmith-detected agent issues into your incident-management, paging, or chat tools. [LangSmith Engine](/langsmith/engine) sends a webhook event to your endpoint when it opens a new issue, or when it links a new trace to an issue it has already opened.

To configure webhook subscriptions, open the **Engine Settings** panel on the **Engine** tab of a tracing project. See [Configure the LangSmith Engine](/langsmith/engine#configure-the-langsmith-engine).

## Delivery

LangSmith sends a `POST` request with a JSON body to your webhook URL. The request uses `Content-Type: application/json` and includes any custom headers you attached to the subscription.

| Property  | Value                                                                                                                                                                                                      |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Method    | `POST`                                                                                                                                                                                                     |
| Body      | JSON, [common envelope](#event-envelope) below                                                                                                                                                             |
| Scheme    | `http://` and `https://` are accepted. `https://` is strongly recommended                                                                                                                                  |
| Signature | `X-LangSmith-Signature` header, signed with the subscription's signing secret                                                                                                                              |
| Timeout   | 20 seconds per attempt                                                                                                                                                                                     |
| Attempts  | Up to 4 attempts (1 initial plus 3 retries with exponential backoff) on transport errors, HTTP `408`, `425`, `429`, and any HTTP `5xx`. Other `4xx` responses are treated as permanent and are not retried |
| Response  | Success is determined from the status code alone. Response bodies are ignored.                                                                                                                             |

<Note>
  Retries deliver a byte-identical payload, including the same `id`. Dedupe on `id` so a retried delivery does not produce a duplicate downstream effect.
</Note>

### Custom headers

You can attach arbitrary headers to each subscription (for example, `Authorization: Bearer …`) to authenticate the caller at your endpoint. `Content-Type` is always set by LangSmith and cannot be overridden.

### Signing secret

Each subscription has a signing secret. LangSmith uses this secret to sign the raw webhook request body and sends the result in the `X-LangSmith-Signature` header.

The header value has this format:

```text theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
sha256=<hex-encoded HMAC-SHA256 digest>
```

Verify the signature before parsing or acting on the payload. The HMAC input is the exact raw request body bytes, and the HMAC key is the subscription's signing secret. Do not parse and reserialize the JSON body before verification.

<CodeGroup>
  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import hashlib
  import hmac
  from typing import Optional


  def verify_langsmith_signature(
      *,
      body: bytes,
      signing_secret: str,
      signature_header: Optional[str],
  ) -> bool:
      if not signature_header or not signature_header.startswith("sha256="):
          return False

      expected = "sha256=" + hmac.new(
          signing_secret.encode("utf-8"),
          body,
          hashlib.sha256,
      ).hexdigest()

      return hmac.compare_digest(expected, signature_header)
  ```

  ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createHmac, timingSafeEqual } from "node:crypto";

  export function verifyLangSmithSignature({
    body,
    signingSecret,
    signatureHeader,
  }: {
    body: Buffer;
    signingSecret: string;
    signatureHeader: string | undefined;
  }) {
    if (!signatureHeader?.startsWith("sha256=")) {
      return false;
    }

    const expected = `sha256=${createHmac("sha256", signingSecret)
      .update(body)
      .digest("hex")}`;

    const expectedBytes = Buffer.from(expected);
    const actualBytes = Buffer.from(signatureHeader);

    return (
      expectedBytes.length === actualBytes.length &&
      timingSafeEqual(expectedBytes, actualBytes)
    );
  }
  ```
</CodeGroup>

### Roll a signing secret

Roll a signing secret when it may have been exposed, or when your organization's credential rotation policy requires a new secret.

To roll a secret, open the subscription row in **Engine Settings**, click **Roll signing secret**, and confirm. LangSmith generates a new signing secret and uses it for future webhook deliveries immediately. The previous secret stops signing deliveries as soon as the roll completes.

After rolling the secret, update every consumer that verifies `X-LangSmith-Signature` with the new value.

### Severity filtering

Each subscription has a `severity_threshold` from `0` to `3`. For issue events, an event is delivered only when the issue's `severity` is less than or equal to the threshold. Lower numbers are more urgent.

| Severity | Meaning |
| -------- | ------- |
| `0`      | Urgent  |
| `1`      | High    |
| `2`      | Medium  |
| `3`      | Low     |

For example, a subscription with `severity_threshold: 1` receives events for `URGENT` (0) and `HIGH` (1) issues only.

Severity thresholds do not apply to [`issue.agent_run.failed`](#issue-agent-run-failed), because run-failure events are scoped to the Engine session rather than to a specific issue.

### Event-type filtering

Each subscription specifies the [event types](#event-types) it wants to receive. Subscriptions created without an explicit list default to `["issue.created"]`.

## Event envelope

Every event delivered to your endpoint uses the same outer JSON shape.

| Field        | Type    | Description                                                                                                                                              |
| ------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`         | UUID    | Unique identifier for this delivery. Stable across retries. Use it to dedupe.                                                                            |
| `type`       | string  | Event type. One of [`issue.created`](#issue-created), [`issue.trace.added`](#issue-trace-added), or [`issue.agent_run.failed`](#issue-agent-run-failed). |
| `created`    | integer | Unix seconds (UTC) when the event was enqueued.                                                                                                          |
| `request_id` | UUID    | Shared by every event fired from the same upstream action. See [Batch coalescing](#batch-coalescing).                                                    |
| `data`       | object  | Event payload. Always contains `data.object`. Contains [`data.trace`](#data-trace) only on [`issue.trace.added`](#issue-trace-added) events.             |

### Issue `data.object`

For [`issue.created`](#issue-created) and [`issue.trace.added`](#issue-trace-added), `data.object` is a snapshot of the issue. Treat it as the authoritative state of the issue at the time the event was generated.

| Field          | Type    | Description                                                                    |
| -------------- | ------- | ------------------------------------------------------------------------------ |
| `id`           | UUID    | Issue ID.                                                                      |
| `name`         | string  | Short title of the issue.                                                      |
| `description`  | string  | Human-readable description.                                                    |
| `severity`     | integer | `0` (urgent) through `3` (low). See [Severity filtering](#severity-filtering). |
| `tenant_id`    | UUID    | Workspace the issue belongs to.                                                |
| `tenant_name`  | string  | Workspace display name.                                                        |
| `session_id`   | UUID    | Tracing project the issue belongs to.                                          |
| `session_name` | string  | Tracing project name.                                                          |
| `url`          | string  | Deep link to the issue in the LangSmith UI.                                    |

### Run failure `data.object`

For [`issue.agent_run.failed`](#issue-agent-run-failed), `data.object` describes the Engine run that failed.

| Field           | Type   | Description                                               |
| --------------- | ------ | --------------------------------------------------------- |
| `tenant_id`     | UUID   | Workspace the run belongs to.                             |
| `tenant_name`   | string | Workspace display name.                                   |
| `session_id`    | UUID   | Tracing project the run belongs to.                       |
| `session_name`  | string | Tracing project name.                                     |
| `url`           | string | Deep link to the LangSmith project in the UI.             |
| `thread_id`     | string | Engine thread ID.                                         |
| `run_id`        | string | Engine run ID. Omitted when unavailable.                  |
| `status`        | string | Final run status.                                         |
| `error_message` | string | Error text from the failed run. Omitted when unavailable. |
| `occurred_at`   | string | RFC 3339 timestamp of when the failure occurred.          |

### `data.trace`

`data.trace` is included only on [`issue.trace.added`](#issue-trace-added) events.

| Field        | Type           | Description                                                           |
| ------------ | -------------- | --------------------------------------------------------------------- |
| `run_id`     | UUID           | ID of the run that was linked to the issue.                           |
| `trace_id`   | UUID           | ID of the trace that contains the run.                                |
| `start_time` | string         | RFC 3339 timestamp of when the run started.                           |
| `comment`    | string \| null | Optional note recorded when the trace was linked. Omitted when empty. |

### Batch coalescing

A single upstream action can produce multiple webhook events. When the Engine opens a new issue and attaches five traces to it, you receive one [`issue.created`](#issue-created) event and five [`issue.trace.added`](#issue-trace-added) events, all sharing the same `request_id`. Use `request_id` to group these into a single downstream notification.

## Event types

The event types below are the complete set the LangSmith Engine sends today. New types may be added in the future, so handlers should ignore unknown `type` values rather than failing.

### `issue.created`

Sent when the LangSmith Engine creates a new issue. `data.trace` is omitted.

```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
{
  "id": "b91c1f0e-7c4a-4f53-9d3e-9f1c8e7a2b10",
  "type": "issue.created",
  "created": 1747238400,
  "request_id": "0d2f4f6a-2a3a-4b6e-9b87-5d5b6e8c9a01",
  "data": {
    "object": {
      "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
      "name": "Tool selection inconsistency",
      "description": "Agent repeatedly calls the search tool with identical arguments before terminating.",
      "severity": 1,
      "tenant_id": "11111111-2222-3333-4444-555555555555",
      "tenant_name": "Acme Workspace",
      "session_id": "66666666-7777-8888-9999-aaaaaaaaaaaa",
      "session_name": "prod-api",
      "url": "https://smith.langchain.com/o/11111111-2222-3333-4444-555555555555/projects/p/66666666-7777-8888-9999-aaaaaaaaaaaa?tab=5&issue=9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"
    }
  }
}
```

### `issue.trace.added`

Sent when a new trace is linked to an existing issue. `data.trace` describes the linked trace.

```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
{
  "id": "c02e3a4b-5c6d-7e8f-9a0b-1c2d3e4f5a6b",
  "type": "issue.trace.added",
  "created": 1747238410,
  "request_id": "0d2f4f6a-2a3a-4b6e-9b87-5d5b6e8c9a01",
  "data": {
    "object": {
      "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
      "name": "Tool selection inconsistency",
      "description": "Agent repeatedly calls the search tool with identical arguments before terminating.",
      "severity": 1,
      "tenant_id": "11111111-2222-3333-4444-555555555555",
      "tenant_name": "Acme Workspace",
      "session_id": "66666666-7777-8888-9999-aaaaaaaaaaaa",
      "session_name": "prod-api",
      "url": "https://smith.langchain.com/o/11111111-2222-3333-4444-555555555555/projects/p/66666666-7777-8888-9999-aaaaaaaaaaaa?tab=5&issue=9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"
    },
    "trace": {
      "run_id": "f1e2d3c4-b5a6-9788-6655-44332211ffee",
      "trace_id": "abcdefab-1234-5678-9abc-def012345678",
      "start_time": "2026-05-14T12:30:00Z",
      "comment": "Reproduces the same tool-loop pattern."
    }
  }
}
```

### `issue.agent_run.failed`

Sent when the LangSmith Engine fails to complete a run. This event is session-scoped, so it does not include `data.trace` and does not use severity filtering.

```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
{
  "id": "4d0e8db2-81e6-4491-b8e5-b13a8f5afc0d",
  "type": "issue.agent_run.failed",
  "created": 1747238500,
  "request_id": "f6bbd48a-0386-403d-9344-31051264b45f",
  "data": {
    "object": {
      "tenant_id": "11111111-2222-3333-4444-555555555555",
      "tenant_name": "Acme Workspace",
      "session_id": "66666666-7777-8888-9999-aaaaaaaaaaaa",
      "session_name": "prod-api",
      "url": "https://smith.langchain.com/o/11111111-2222-3333-4444-555555555555/projects/p/66666666-7777-8888-9999-aaaaaaaaaaaa",
      "thread_id": "thread-123",
      "run_id": "run-456",
      "status": "error",
      "error_message": "RuntimeError: missing API key",
      "occurred_at": "2026-05-14T12:45:00Z"
    }
  }
}
```

## Test your endpoint

Before pointing a real subscription at your endpoint, send a sample payload to verify it accepts and acknowledges within the 20-second timeout:

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
curl -X POST https://your-endpoint.example.com/webhook \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $WEBHOOK_SECRET" \
  -d @sample-issue-created.json
```

Use the example body from [`issue.created`](#issue-created) as `sample-issue-created.json`. Verify that:

* The custom `Authorization` header arrives and matches the secret you configured on the subscription.
* The handler persists the event keyed by its `id` so retries are deduped.
* The handler returns `2xx` before kicking off slow downstream work.

## Security

* Webhook URLs are validated when the subscription is created and again at delivery time. Private and metadata IP ranges are blocked in SaaS. Both `http://` and `https://` are accepted; use `https://` so the payload and any custom headers are not sent in cleartext.
* LangSmith signs webhook bodies with the subscription's signing secret. Verify `X-LangSmith-Signature` before processing the payload.
* You can also set custom headers on the subscription, such as `Authorization: Bearer …`, for routing or additional authentication at your endpoint.
* Dedupe on the event `id` so that a retried delivery does not cause a duplicate notification.

## Best practices

* **Acknowledge fast.** Respond with `2xx` as soon as you have persisted the event. Move slow work (fan-out, paging, downstream API calls) onto a queue so your handler stays within the 20-second timeout.
* **Tolerate unknown event types.** Ignore `type` values your handler does not recognize. New event types may be added without notice.
* **Tolerate new fields.** Parse payloads with a permissive schema. New fields may be added to existing event types without notice.

***

<div className="source-links">
  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>

  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/engine-webhooks.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>
</div>
