Webhooks
Send signed JSON to your own URL when feedback is created, changed or commented on.
Updated October 2026
Set up
In Project → Settings → Integrations, add a Custom Webhook with your endpoint URL. PinMarks generates a secret (whsec_…) used to sign each request. Only owners and admins can manage it, and there is one webhook per project. Use Send test to deliver a ping event.
Events
| Event | When |
|---|---|
feedback.created | New feedback submitted. |
feedback.status_changed | Status changed. |
feedback.priority_changed | Priority changed. |
feedback.type_changed | Type changed. |
feedback.assignee_changed | Assignee changed. |
comment.created | New public comment (internal notes are never sent). |
Payload
{"id": "evt_...","event": "feedback.status_changed","created_at": "2026-10-13T09:30:00Z","project": { "id": "...", "name": "Acme website" },"data": {"feedback": { "title": "...", "status": "resolved", "priority": "high", "page_url": "..." },"actor": { },"changes": { "status": { "from": "open", "to": "resolved" } }}}
Requests carry X-Pinmarks-Event, X-Pinmarks-Event-Id (use it to de-duplicate), X-Pinmarks-Delivery, X-Pinmarks-Timestamp and X-Pinmarks-Signature.
Verify the signature
The signature header looks like t=<unix>,v1=<hex>. It is an HMAC-SHA256 of <timestamp>.<raw body> using your secret. Reject requests older than 5 minutes to prevent replays.
import crypto from 'crypto';function verify(rawBody, header, secret) {const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));}
Delivery and retries
- Your endpoint must answer with a 2xx within 10 seconds. Redirects are not followed.
- Failed deliveries are retried up to 3 times: immediately, after 30 seconds, then after 5 minutes.
- The delivery log keeps 30 days and lets you redeliver. You can regenerate the secret at any time.
- Private and internal addresses are blocked, so use a public HTTPS URL.
Still stuck?
Email support@pinmarks.io and we'll get back to you within one business day.
