curl -sS https://tycoon.us/api/public/webhooks \
-H "Authorization: Bearer $TYCOON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/webhooks/tycoon",
"events": ["task.check_in", "task.requires_action", "task.done", "task.canceled"]
}'
A callback supplied at Task creation is separate: it is attached only to
that Task and is not listed or managed through the workspace-wide webhook API.
It receives the same signed public-safe events without allowing one customer's
callback to receive another customer's Task.
Verify the raw request body with HMAC-SHA256 and compare it with
x-tycoon-signature in the form sha256=<hex>. Use
x-tycoon-delivery as the stable per-transition, per-endpoint delivery
identity, including for legacy Task close paths — a receiver that already
processed a delivery id can safely ignore a repeat of it. task.check_in is
sent after a material canonical checkpoint with a safe outcome projection.
task.requires_action additionally contains the safe primary ApprovalRequest
summary, current action version, safe decision items, api_resolvable, and an
authenticated action_url; when it is API-resolvable, call the resolve
endpoint above, or use POST /tasks/{id}/messages to add context. Terminal payloads contain
safe Task identity and status; call GET /tasks/{id} for the normalized result,
outcome, and usage.
Node.js verification example:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyTycoonWebhook(rawBody, signature, secret) {
const expected = `sha256=${createHmac("sha256", secret)
.update(rawBody)
.digest("hex")}`;
const left = Buffer.from(signature ?? "");
const right = Buffer.from(expected);
return left.length === right.length && timingSafeEqual(left, right);
}