Skip to content
Feeblo Docs
Esc
navigateopen⌘Jpreview
On this page

Webhooks

Receive feedback.post.created and feedback.post.status_changed events at your HTTPS endpoint, verify Standard Webhooks signatures, and handle retries.

Webhooks push feedback activity to your server as it happens. Register an HTTPS endpoint in the Feeblo dashboard, pick the events you want, and your receiver gets a signed JSON request each time something happens on a board. Use this page when you need to verify signatures, deduplicate deliveries, or debug why an endpoint stopped receiving requests.

Events

V1 emits two event types:

Event Fires when
feedback.post.created Someone submits feedback on a board
feedback.post.status_changed A moderator moves a post to a new status

You select these per route when you register the endpoint. The dashboard’s “send test” button delivers a webhook.test event instead. It travels through the same delivery and signing path as real events, but you can’t subscribe a route to it.

Payload

Every request carries a versioned JSON envelope:

{
  "id": "iev_example",
  "organizationId": "org_example",
  "type": "feedback.post.created",
  "version": 1,
  "occurredAt": "2026-08-11T00:00:00.000Z",
  "post": {
    "id": "pst_example",
    "title": "Example",
    "url": "https://app.feeblo.com/org_example/post/feedback/example"
  },
  "board": { "id": "brd_feedback", "name": "Feedback", "slug": "feedback" },
  "status": { "id": "pss_open", "type": "PENDING" },
  "actor": { "type": "end_user" }
}

The blocks break down like this:

  • post holds the post ID, title, and absolute URL. status.type is one of PENDING, REVIEW, PLANNED, IN_PROGRESS, COMPLETED, or CLOSED.
  • board identifies the board the post lives on.
  • previousStatus appears on feedback.post.status_changed deliveries and names the status the post moved out of.
  • actor classifies who caused the event: end_user for someone submitting through the widget or public board, member for a signed-in team member. Member events carry an optional memberId and displayName.

Verifying signatures

Each request carries Standard Webhooks headers plus one Feeblo-specific header:

Header Meaning
webhook-id Stable ID for this delivery. Survives retries.
webhook-timestamp Unix seconds for this attempt. Regenerated on every retry.
webhook-signature HMAC signature for this attempt.
x-feeblo-event Event type, e.g. feedback.post.created.

Requests arrive as application/json with User-Agent: Feeblo-Webhooks/1. Verify the exact raw request bytes before you parse anything. A receiver that parses first and verifies later accepts tampered bodies.

Install the standardwebhooks library, then verify with the secret Feeblo showed you at registration:

npm install standardwebhooks
pnpm add standardwebhooks
yarn add standardwebhooks
bun add standardwebhooks
import { Webhook } from "standardwebhooks";

const webhook = new Webhook(process.env.FEEBLO_WEBHOOK_SECRET!);
const event = webhook.verify(rawBody, {
  "webhook-id": request.headers.get("webhook-id")!,
  "webhook-timestamp": request.headers.get("webhook-timestamp")!,
  "webhook-signature": request.headers.get("webhook-signature")!,
});

Secrets start with whsec_. Pass the whole string, prefix included.

Rotating the signing secret

Rotate from the dashboard whenever someone leaves the team or a secret leaks. Feeblo generates a new secret and shows it once. Store it immediately; nobody can retrieve it afterward.

For 24 hours after rotation, Feeblo signs each delivery with both the new and the old key, so in-flight deliveries stay verifiable while you deploy. During that window keep both secrets configured and try each until verification succeeds. Once 24 hours pass, drop the old secret.

Retries and idempotency

Delivery is at-least-once. Your endpoint can receive the same delivery more than once, so treat the webhook-id header as an idempotency key: skip processing when you’ve seen the ID before, and return any 2xx status after successful handling.

A failed attempt schedules retries roughly 1 minute, 5 minutes, 30 minutes, 2 hours, 8 hours, and 24 hours later, with jitter of up to ±20% around each delay. That makes seven attempts including the original request. Statuses 408, 409, 425, 429, and all 5xx codes retry; other 3xx and 4xx codes end the delivery permanently. A valid Retry-After header on a 429 response overrides the schedule, capped at 24 hours.

Endpoint requirements

Feeblo validates endpoints before saving them and again before every delivery:

  • Production endpoints must use HTTPS. Development receivers may use plain HTTP.
  • The URL can’t contain credentials or a fragment.
  • Hostnames must resolve to public addresses. Feeblo rejects localhost, private and reserved ranges, and cloud-metadata destinations.
  • Feeblo resolves DNS once per delivery and pins the connection to those addresses, so a hostname can’t swap to a private address between validation and connection.
  • Feeblo doesn’t follow redirects. Point the endpoint directly at your receiver.

Developing against a receiver on localhost? Set INTEGRATION_ALLOW_PRIVATE_NETWORK=true in your Feeblo environment. The override only works when Feeblo runs in development mode and defaults to false.

Next steps

Was this page helpful?