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

# Webhooks

> Receive real-time event notifications from Jinba Toolbox via webhooks

## Overview

Webhooks allow you to receive HTTP notifications whenever significant events occur in your organization. Instead of polling the API, you register a URL and Jinba Toolbox pushes event payloads to it in real time.

Webhooks are configured **per organization** and can subscribe to one or more event types.

## Event Types

| Event                | Description                                |
| -------------------- | ------------------------------------------ |
| `tool.run.completed` | A tool execution finished successfully     |
| `tool.run.failed`    | A tool execution failed                    |
| `toolset.published`  | A new toolset version was published        |
| `member.added`       | A new member was added to the organization |
| `member.removed`     | A member was removed from the organization |

## Setting Up Webhooks

<Steps>
  <Step title="Create a webhook endpoint">
    Set up an HTTP endpoint on your server that can receive POST requests. The endpoint must return a `2xx` status code to acknowledge receipt.
  </Step>

  <Step title="Register the webhook">
    Use the API or the web console to register your endpoint URL and select the events you want to subscribe to.

    ```bash theme={null}
    curl -X POST https://toolbox-api.jinba.dev/v1/orgs/{orgId}/webhooks \
      -H "Authorization: Bearer jtb_xxxxxxxxxxxx" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://example.com/webhooks/jinba",
        "events": ["tool.run.completed", "tool.run.failed"]
      }'
    ```
  </Step>

  <Step title="Store the signing secret">
    When you create a webhook, a signing secret is generated. Store it securely -- you will use it to verify incoming payloads.
  </Step>

  <Step title="Send a test event">
    Verify your endpoint is working by sending a test event:

    ```bash theme={null}
    curl -X POST https://toolbox-api.jinba.dev/v1/orgs/{orgId}/webhooks/{webhookId}/test \
      -H "Authorization: Bearer jtb_xxxxxxxxxxxx"
    ```
  </Step>
</Steps>

## Webhook Configuration

Each webhook has the following properties:

```typescript theme={null}
interface Webhook {
  id: string;
  orgId: string;
  url: string;           // Destination URL
  events: string[];      // Subscribed event types
  secret: string;        // HMAC signing secret
  enabled: boolean;      // Whether the webhook is active
  createdAt: Date;
}
```

## Payload Format

Every webhook delivery sends a JSON payload with the following structure:

```json theme={null}
{
  "event": "tool.run.completed",
  "timestamp": "2026-02-01T12:00:00Z",
  "data": {
    "runId": "run_abc123",
    "toolSetSlug": "slack-tools",
    "toolSlug": "post-message",
    "status": "success",
    "durationMs": 1234
  }
}
```

### Event-Specific Payloads

**`tool.run.completed`**

```json theme={null}
{
  "event": "tool.run.completed",
  "timestamp": "2026-02-01T12:00:00Z",
  "data": {
    "runId": "run_abc123",
    "toolSetSlug": "slack-tools",
    "toolSlug": "post-message",
    "status": "success",
    "durationMs": 1234
  }
}
```

**`tool.run.failed`**

```json theme={null}
{
  "event": "tool.run.failed",
  "timestamp": "2026-02-01T12:05:00Z",
  "data": {
    "runId": "run_def456",
    "toolSetSlug": "slack-tools",
    "toolSlug": "post-message",
    "status": "failed",
    "error": {
      "name": "SlackAPIError",
      "message": "channel_not_found"
    },
    "durationMs": 567
  }
}
```

**`toolset.published`**

```json theme={null}
{
  "event": "toolset.published",
  "timestamp": "2026-02-01T10:00:00Z",
  "data": {
    "toolSetSlug": "slack-tools",
    "version": "1.3.0",
    "publishedBy": "user_abc123",
    "releaseNotes": "Added thread reply support"
  }
}
```

**`member.added`**

```json theme={null}
{
  "event": "member.added",
  "timestamp": "2026-02-01T09:00:00Z",
  "data": {
    "userId": "user_xyz789",
    "email": "newmember@example.com",
    "role": "member"
  }
}
```

## Signature Verification

Every webhook request includes an `X-Webhook-Signature` header containing an HMAC-SHA256 signature of the request body, computed with your webhook's signing secret.

```
X-Webhook-Signature: sha256=a1b2c3d4e5f6...
```

**Verify the signature** on your server to ensure payloads have not been tampered with:

```typescript theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expected = "sha256=" + createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  return timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your request handler
const isValid = verifyWebhookSignature(
  requestBody,
  request.headers["x-webhook-signature"],
  process.env.WEBHOOK_SECRET!
);

if (!isValid) {
  return new Response("Invalid signature", { status: 401 });
}
```

## Webhook Management API

| Method | Endpoint                            | Description     |
| ------ | ----------------------------------- | --------------- |
| GET    | `/v1/orgs/:orgId/webhooks`          | List webhooks   |
| POST   | `/v1/orgs/:orgId/webhooks`          | Create webhook  |
| PATCH  | `/v1/orgs/:orgId/webhooks/:id`      | Update webhook  |
| DELETE | `/v1/orgs/:orgId/webhooks/:id`      | Delete webhook  |
| POST   | `/v1/orgs/:orgId/webhooks/:id/test` | Send test event |

## Retry Behavior

If your endpoint does not return a `2xx` status code, Jinba Toolbox retries the delivery:

| Attempt   | Delay      |
| --------- | ---------- |
| 1st retry | 1 minute   |
| 2nd retry | 5 minutes  |
| 3rd retry | 30 minutes |

After 3 consecutive failures, the webhook is automatically **disabled**. You can re-enable it manually from the web console or via the API once the underlying issue is resolved.

## Best Practices

* **Always verify the signature** before processing a payload to prevent tampering.
* **Return a `200` status quickly** -- perform any heavy processing asynchronously after acknowledging the webhook.
* **Use idempotent processing** -- webhook deliveries may arrive more than once due to retries. Use the `runId` or event timestamp to deduplicate.
* **Monitor webhook health** -- re-enable disabled webhooks promptly and investigate delivery failures.
* **Subscribe only to needed events** -- reduce noise by selecting only the events your integration requires.

## Related

* [REST API](/en/pages/toolbox/developer/api) -- Full endpoint reference
* [Security & Access Control](/en/pages/toolbox/advanced/security) -- Authentication and authorization details
