A webhook sends a POST to your endpoint each time something happens in your project, such as a new post or a message from a user. Use it to tell your other tools. Webhooks work on every plan; owners and admins set them up.
Add a webhook
Open Settings → Developers.
Under Webhooks, enter your endpoint's address. It must start with
https://.Untick the events you don't want; they're all ticked to start with. Pick at least one.
Choose Add webhook.

Copy the signing secret straight away with Copy secret: it's shown only once. You'll need it to check that requests come from Tuti.
A project can have up to 10 webhooks. A webhook's address and events can't be edited: add a new one and delete the old one instead. That's also how you get a new secret.
Events
Event | In settings | Sent when |
|---|---|---|
| New post | Someone posts feedback, on the portal, in the widget or through the API. |
| Status changed | A post's status changes. The data has the post and its |
| New comment | A comment is added to a post. |
| Changelog published | A changelog entry is published. |
| Autopilot found updates | A merged pull request has updates to posts or articles waiting for your review. The data says how many wait and links to them in Autopilot. |
| New conversation | A new conversation starts in your inbox. The data includes its first message. |
| New message from a user | A user writes again in a conversation. Your team's replies don't send it. |
Send test, next to a webhook, sends it a ping event.
What Tuti sends
Each request is a POST with a JSON body:
{
"id": "evt_…",
"type": "post.created",
"createdAt": "2026-11-05T09:30:00.000Z",
"project": { "slug": "fieldnote", "name": "Fieldnote" },
"data": { "number": 12, "title": "Export notes to PDF", "status": "open" }
}data is shaped as the API returns the same thing; the API reference has the details. Each request also has these headers:
Tuti-Event-Type: the event, such aspost.created.Tuti-Event-Id: the same asid, and the same on every retry, so you can skip an event you've already handled.Tuti-Signature:t=<time>,v1=<signature>, where the signature is an HMAC-SHA256, with your secret, of the time, a dot and the raw body.
Check it's from Tuti
Check the signature before you trust a request. In Node:
import { createHmac, timingSafeEqual } from "node:crypto";
// body: the raw request body as a string, before parsing it.
function verify(body, header, secret) {
const { t, v1 } = Object.fromEntries((header ?? "").split(",").map((part) => part.split("=")));
if (!/^\d+$/.test(t ?? "") || !/^[0-9a-f]{64}$/.test(v1 ?? "")) return false;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // older than 5 minutes
const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest("hex");
return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}Deliveries and retries
Answer with any
2xxwithin 10 seconds.Anything else, or no answer, is retried up to 8 times with growing gaps: after a few seconds at first, then up to 8 hours apart, about 21 hours in all.
An event can arrive more than once, so use its id to skip repeats.
Redirects aren't followed, and the address must point to a public server.
Each webhook in the list shows its events and how its latest delivery went: "last delivered 14 minutes ago", or "last delivery failed 2 minutes ago:" with the reason, such as "Answered 503".
Delete a webhook
Choose Delete next to it, then Delete webhook. It stops getting events right away, including retries still waiting.
To read data rather than wait for events, see Use the API and MCP.

