Cookies
We use cookies to keep the site working, and optional cookies for analytics and marketing.
Developers

Outbound webhooks

FeedbackHive can send an HTTPS request to your server when feedback arrives, a task changes or someone comments. Webhooks are included in the Pro plan.

Set up an endpoint

In Settings, Webhooks, add an HTTPS URL and pick the events it should receive. FeedbackHive shows the signing secret once, so store it right away. The same page sends test deliveries, disables or edits an endpoint, rotates its secret and shows the delivery log.

A workspace can have up to 10 endpoints. Events cover the projects the workspace owns, and only the workspace owner can manage endpoints.

Events

feedback.created

New feedback arrived from the widget, a public feedback form or a public project.

task.created

A task was created any other way: in the app, through the API, CLI, MCP or WhatsApp, by AI, or from a calendar event.

task.updated

The title, description, priority, due date, assignee, group or tags changed. changedFields lists which.

task.status_changed

A task moved to another status. previousStatus holds the status it left.

comment.created

Someone commented on a task, including guests in the widget.

A change that touches the status and other fields sends both task.status_changed and task.updated. Bulk side effects, such as deleting a task group or removing a status, send nothing. The test button sends webhook.test.

Payload

Every request is a JSON POST with the same envelope: version, id, event, timestamp and data. New fields can appear within a version, so ignore the ones you don't know. A breaking change would raise the version.

task.status_changed
{
  "version": 1,
  "id": "evt_5f0c1d2e3a4b5c6d7e8f9a0b",
  "event": "task.status_changed",
  "timestamp": "2026-11-01T09:30:00.000Z",
  "data": {
    "project": { "id": "p4Zt7bNq2W", "title": "Client website" },
    "task": {
      "id": "b7Kd2mQx9P",
      "title": "Fix the contact form",
      "description": "<p>The submit button does nothing on mobile.</p>",
      "status": { "id": "done", "name": "Done" },
      "priority": "high",
      "dueDate": null,
      "assignee": { "id": "h3Nw8rLc1T", "name": "Alex" },
      "groupId": null,
      "tags": ["bug"],
      "url": "https://feedbackhive.app/en/projects/p4Zt7bNq2W#tasks/b7Kd2mQx9P",
      "createdAt": "2026-10-30T14:02:11.000Z",
      "updatedAt": "2026-11-01T09:30:00.000Z"
    },
    "actor": { "id": "h3Nw8rLc1T", "name": "Alex" },
    "previousStatus": { "id": "in_progress", "name": "In progress" }
  }
}

Beyond project and task: task.updated adds changedFields, the two created events add source, and comment.created carries a comment object and a shorter task. Descriptions and comments are HTML, capped at 100,000 characters. Prices, time estimates and email addresses are never included.

Headers

  • X-FeedbackHive-Event: the event name.
  • X-FeedbackHive-Event-Id: the same as id in the body. It doesn't change between retries, so use it to ignore duplicates.
  • X-FeedbackHive-Delivery: this event's delivery to this endpoint.
  • X-FeedbackHive-Signature: t=<unix seconds>,v1=<hex signature>.

Verify the signature

v1 is the HMAC-SHA256 of <t>.<raw body>, keyed with the endpoint's secret. Compute it over the raw bytes you received, before any JSON parsing, and reject timestamps more than five minutes away from your clock. Each attempt is signed with a fresh timestamp.

Node.js
import { createHmac, timingSafeEqual } from "node:crypto"

const TOLERANCE_SECONDS = 300

export function isValidWebhook(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((part) => part.split("=", 2))
  )
  const timestamp = Number(parts.t)
  if (!Number.isInteger(timestamp) || !parts.v1) return false

  // Reject replays of an old, captured request.
  const age = Math.abs(Date.now() / 1000 - timestamp)
  if (age > TOLERANCE_SECONDS) return false

  const expected = Buffer.from(
    createHmac("sha256", secret)
      .update(`${timestamp}.${rawBody}`)
      .digest("hex")
  )
  const received = Buffer.from(parts.v1)
  return (
    expected.length === received.length &&
    timingSafeEqual(expected, received)
  )
}
Python
import hashlib, hmac, time

TOLERANCE_SECONDS = 300

def is_valid_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
    parts = dict(part.split("=", 1) for part in signature_header.split(","))
    try:
        timestamp = int(parts["t"])
        received = parts["v1"]
    except (KeyError, ValueError):
        return False

    # Reject replays of an old, captured request.
    if abs(time.time() - timestamp) > TOLERANCE_SECONDS:
        return False

    signed = f"{timestamp}.".encode() + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received)

Rotating a secret takes effect immediately. Deliveries that fail verification in the meantime are retried as usual.

Delivery and retries

Answer with a 2xx status within 10 seconds. Anything else counts as a failure: other status codes, timeouts, connection or TLS errors. Redirects are not followed, so a 3xx is a failure too. Do slow work after responding.

A failed delivery is retried up to 7 times, after about 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 24 hours. Deliveries can arrive out of order and, rarely, more than once.

Automatic disabling

An endpoint that has failed at least 15 attempts in a row over three days or more is turned off. The workspace owner gets an email and the settings page shows the state. Enabling it again resets the count. Events that happen while an endpoint is off are not sent later.

Delivery log

The log shows the status, attempt count and response code of the last 50 deliveries per endpoint. Entries are kept for 30 days, and payloads are removed as soon as a delivery finishes.

Network rules

The URL must use HTTPS with a valid certificate. Hosts that resolve to private, loopback, link-local or cloud metadata addresses are refused, when you save the URL and again on every delivery.

Plan changes

If a workspace leaves the Pro plan, its endpoints are kept but paused: no events are sent, queued deliveries are dropped and only viewing or deleting endpoints remains possible. Upgrading resumes them for new events.