Skip to main content

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

EventDescription
tool.run.completedA tool execution finished successfully
tool.run.failedA tool execution failed
toolset.publishedA new toolset version was published
member.addedA new member was added to the organization
member.removedA member was removed from the organization

Setting Up Webhooks

1

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.
2

Register the webhook

Use the API or the web console to register your endpoint URL and select the events you want to subscribe to.
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"]
  }'
3

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.
4

Send a test event

Verify your endpoint is working by sending a test event:
curl -X POST https://toolbox-api.jinba.dev/v1/orgs/{orgId}/webhooks/{webhookId}/test \
  -H "Authorization: Bearer jtb_xxxxxxxxxxxx"

Webhook Configuration

Each webhook has the following properties:
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:
{
  "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
{
  "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
{
  "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
{
  "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
{
  "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:
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

MethodEndpointDescription
GET/v1/orgs/:orgId/webhooksList webhooks
POST/v1/orgs/:orgId/webhooksCreate webhook
PATCH/v1/orgs/:orgId/webhooks/:idUpdate webhook
DELETE/v1/orgs/:orgId/webhooks/:idDelete webhook
POST/v1/orgs/:orgId/webhooks/:id/testSend test event

Retry Behavior

If your endpoint does not return a 2xx status code, Jinba Toolbox retries the delivery:
AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 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.