Overview

Webhooks let you receive HTTP POST notifications when events occur in ProBeya. Instead of polling the API for changes, register a webhook endpoint and ProBeya will push events to you in real time — typically within 1–3 seconds of the event occurring.

Use webhooks to:

  • Sync ProBeya data with external systems (ERP, MES, QMS)
  • Trigger CI/CD pipelines when items change status
  • Build custom alerting and notification flows
  • Update external dashboards when KPIs are recorded

Setting Up a Webhook

Via the Dashboard

  1. Navigate to Settings > Integrations > Webhooks
  2. Click + Create Webhook
  3. Enter the endpoint URL (must be HTTPS in production)
  4. Select the events you want to receive
  5. Set a signing secret (strongly recommended for payload verification)
  6. Click Create

Via the API

curl -X POST "https://acme.probeya.com/api/v1/webhooks" \
  -H "Authorization: Bearer probeya_sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/probeya",
    "events": ["item.created", "item.updated", "kpi.value_entered"],
    "secret": "whsec_your_signing_secret_here",
    "description": "Sync to SAP"
  }'

Response:

{
  "data": {
    "id": "whk_clx9abc123",
    "url": "https://your-server.com/webhooks/probeya",
    "events": ["item.created", "item.updated", "kpi.value_entered"],
    "active": true,
    "createdAt": "2026-03-19T14:30:00.000Z"
  }
}

Event Types

Items

EventTrigger
item.createdA new item is created on any board
item.updatedAn item’s fields, status, or assignment change
item.deletedAn item is permanently deleted
item.movedAn item is moved to a different group or board
item.commentedA comment is added to an item

KPIs

EventTrigger
kpi.value_enteredA KPI measurement value is recorded
kpi.target_breachedA KPI value crosses its target threshold (red/amber)
kpi.createdA new KPI definition is created
kpi.updatedA KPI definition is modified

Actions

EventTrigger
action.createdA new action item is created
action.updatedAn action’s status, owner, or due date changes
action.escalatedAn overdue action is escalated to a higher tier
action.completedAn action is marked as completed

Projects & Boards

EventTrigger
project.createdA new project is created
project.deletedA project is deleted
board.createdA new board is created within a project
board.updatedA board’s configuration changes
board.deletedA board is deleted

Members

EventTrigger
member.invitedA member is invited to the organization
member.joinedAn invited member accepts and joins
member.removedA member is removed from the organization
member.role_changedA member’s role is changed

Forms

EventTrigger
form.submittedA form receives a submission
form.publishedA form is published and made available

Meetings

EventTrigger
meeting.startedA tier meeting begins
meeting.completedA tier meeting is concluded

Payload Format

All webhook payloads follow a consistent structure:

{
  "id": "evt_clx9abc123def",
  "event": "item.updated",
  "timestamp": "2026-03-19T14:30:00.000Z",
  "apiVersion": "v1",
  "organizationId": "org_clx9abc123",
  "data": {
    "item": {
      "id": "itm_clx9abc123",
      "name": "Update SOP-42 revision 3",
      "boardId": "brd_clx9xyz789",
      "groupId": "grp_clx9abc123",
      "status": "done",
      "priority": "high",
      "assigneeId": "usr_clx9def456"
    },
    "changes": {
      "status": {
        "old": "in_progress",
        "new": "done"
      }
    },
    "actor": {
      "id": "usr_clx9def456",
      "email": "[email protected]"
    }
  }
}

Payload Fields

FieldTypeDescription
idstringUnique event ID — use for idempotent processing
eventstringEvent type (e.g., item.updated)
timestampstringISO 8601 timestamp of when the event occurred
apiVersionstringAPI version that generated the event
organizationIdstringOrganization where the event occurred
dataobjectEvent-specific payload (varies by event type)
data.changesobjectFor .updated events, the old and new values of changed fields
data.actorobjectThe user who triggered the event

Signature Verification

If you provided a secret when creating the webhook, every request includes an X-ProBeya-Signature header. Always verify this signature to confirm the payload was sent by ProBeya and was not tampered with in transit.

The signature is computed as sha256=<hex-encoded HMAC-SHA256 of the raw request body using your secret as the key>.

import { createHmac, timingSafeEqual } from "crypto";

/**
 * Verify that a webhook payload was genuinely sent by ProBeya.
 * Uses timing-safe comparison to prevent timing attacks.
 */
function verifyWebhookSignature(rawBody, signature, secret) {
  const expected = "sha256=" +
    createHmac("sha256", secret)
      .update(rawBody, "utf8")
      .digest("hex");

  // Timing-safe comparison prevents attackers from guessing the
  // signature one byte at a time via response-time analysis
  const sigBuffer = Buffer.from(signature, "utf8");
  const expectedBuffer = Buffer.from(expected, "utf8");

  if (sigBuffer.length !== expectedBuffer.length) {
    return false;
  }

  return timingSafeEqual(sigBuffer, expectedBuffer);
}

// Express middleware example
app.post("/webhooks/probeya", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-probeya-signature"];
  const rawBody = req.body.toString("utf8");
  const secret = process.env.PROBEYA_WEBHOOK_SECRET;

  if (!verifyWebhookSignature(rawBody, signature, secret)) {
    console.error("Invalid webhook signature — rejecting payload");
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(rawBody);
  console.log(`Received ${event.event} (${event.id})`);

  // Process the event...

  // Return 200 quickly to acknowledge receipt
  res.status(200).json({ received: true });
});

Always use timing-safe comparison (timingSafeEqual in Node.js, hmac.compare_digest in Python) when verifying signatures. Standard string comparison (===, ==) is vulnerable to timing attacks that could allow an attacker to reconstruct the expected signature.

Retry Policy

If your endpoint returns a non-2xx status code or does not respond within 10 seconds, ProBeya retries delivery with exponential backoff:

AttemptDelay After Failure
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry12 hours

After 5 failed attempts, the webhook is marked as failed and no further deliveries are attempted. You can:

  • Fix your endpoint and click Retry Failed in the dashboard
  • View failed deliveries in Settings > Integrations > Webhooks > [name] > Logs

Return a 200 OK response as quickly as possible. Process the event asynchronously (e.g., push to a queue) rather than performing slow operations synchronously — a response timeout counts as a failure.

Idempotent Processing

Webhook deliveries may occasionally be duplicated (e.g., during retries or network issues). Use the id field in the event payload to implement idempotent handling:

const processedEvents = new Set(); // In production, use Redis or a database

app.post("/webhooks/probeya", (req, res) => {
  const event = req.body;

  // Skip events we have already processed
  if (processedEvents.has(event.id)) {
    return res.status(200).json({ received: true, duplicate: true });
  }

  processedEvents.add(event.id);

  // Process the event...

  res.status(200).json({ received: true });
});

Managing Webhooks

List Webhooks

curl "https://acme.probeya.com/api/v1/webhooks" \
  -H "Authorization: Bearer probeya_sk_live_abc123..."

Update a Webhook

curl -X PATCH "https://acme.probeya.com/api/v1/webhooks/whk_clx9abc123" \
  -H "Authorization: Bearer probeya_sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["item.created", "item.updated", "item.deleted"],
    "active": true
  }'

Delete a Webhook

curl -X DELETE "https://acme.probeya.com/api/v1/webhooks/whk_clx9abc123" \
  -H "Authorization: Bearer probeya_sk_live_abc123..."