Skip to main content
Webhooks let fsckmsft push notifications to your server the moment something happens in your workspace — a project is created, a task completes, a workflow finishes. Instead of polling the API on a schedule, your application receives an HTTP POST to a URL you control, carrying a JSON payload that describes exactly what changed.

Create a webhook endpoint

1

Open Webhooks settings

Navigate to Settings → Developer → Webhooks and click Add Endpoint.
2

Enter your endpoint URL

Provide the public HTTPS URL where fsckmsft should deliver events, for example https://yourapp.example.com/webhooks/fsckmsft. The endpoint must be reachable from the internet and must respond with a 2xx status code to acknowledge receipt.
3

Choose events to subscribe to

Select one or more events from the list below. You can also choose Send all events to receive everything — useful during development.
EventTriggered when
project.createdA new project is created in the workspace
project.updatedA project’s name, status, or settings change
project.deletedA project is permanently deleted
task.createdA new task is added to a project
task.updatedA task’s title, assignee, or status changes
task.completedA task is marked as complete
workflow.run.startedA workflow run begins executing
workflow.run.completedA workflow run finishes (success or failure)
workflow.run.failedA workflow run encounters a fatal error
member.invitedA new member is invited to the workspace
member.removedA member is removed from the workspace
4

Save the signing secret

After saving, fsckmsft generates a Signing Secret for this endpoint.
Copy the signing secret now — it is displayed only once. You need it to verify that incoming webhook requests genuinely originate from fsckmsft. Store it in an environment variable on your server, never in source code.
5

Send a test event

Click Send Test Event to deliver a sample ping payload to your endpoint. Confirm your server receives it and returns 200 OK.

Webhook payload structure

Every webhook request is an HTTP POST with a Content-Type: application/json header. The body is a JSON object with a consistent top-level structure:
{
  "id": "evt_01hx9k2pmq3rz5btv8wncjf4gd",
  "event": "task.completed",
  "timestamp": "2024-06-15T14:32:07.812Z",
  "workspace_id": "ws_01hx9k2pmq3rz5btv8wncjf4ga",
  "data": {
    "id": "tsk_01hx9k2pmq3rz5btv8wncjf4gb",
    "title": "Review Q3 proposal",
    "project_id": "prj_01hx9k2pmq3rz5btv8wncjf4gc",
    "completed_by": "usr_01hx9k2pmq3rz5btv8wncjf4ge",
    "completed_at": "2024-06-15T14:32:07.000Z"
  }
}
FieldTypeDescription
idstringUnique identifier for this event delivery attempt
eventstringThe event type, e.g. task.completed
timestampstringISO 8601 UTC timestamp of when the event occurred
workspace_idstringThe workspace that generated the event
dataobjectEvent-specific payload — shape varies by event type
Use the id field to detect and deduplicate retried deliveries. Store processed event IDs and skip any that you have already handled.

Verify webhook signatures

Every request fsckmsft sends to your endpoint includes an X-fsckmsft-Signature header. Verify this header on every incoming request to confirm it originated from fsckmsft and was not tampered with in transit. The signature is an HMAC-SHA256 digest of the raw request body, using your endpoint’s signing secret as the key. It is hex-encoded and prefixed with sha256=.

Node.js / Express example

import crypto from "crypto";
import express from "express";

const app = express();

// Use raw body parser — signature verification requires the unmodified bytes
app.use(
  "/webhooks/fsckmsft",
  express.raw({ type: "application/json" })
);

app.post("/webhooks/fsckmsft", (req, res) => {
  const signingSecret = process.env.FSCKMSFT_WEBHOOK_SECRET;
  const signature = req.headers["x-fsckmsft-signature"];

  if (!signature) {
    return res.status(400).send("Missing signature header");
  }

  const expectedSig =
    "sha256=" +
    crypto
      .createHmac("sha256", signingSecret)
      .update(req.body) // req.body is a Buffer when using express.raw()
      .digest("hex");

  // Use timingSafeEqual to prevent timing attacks
  const sigBuffer = Buffer.from(signature);
  const expectedBuffer = Buffer.from(expectedSig);

  if (
    sigBuffer.length !== expectedBuffer.length ||
    !crypto.timingSafeEqual(sigBuffer, expectedBuffer)
  ) {
    return res.status(401).send("Invalid signature");
  }

  const event = JSON.parse(req.body.toString());
  console.log("Verified event:", event.event, event.id);

  // Process the event...

  res.status(200).send("OK");
});

app.listen(3000);
Always use crypto.timingSafeEqual (or your language’s equivalent constant-time comparison) when comparing the signatures. Using a regular string equality check (===) leaks timing information that attackers can exploit.
import hashlib
import hmac
import os
from flask import Flask, request, abort

app = Flask(__name__)

@app.route("/webhooks/fsckmsft", methods=["POST"])
def webhook():
    secret = os.environ["FSCKMSFT_WEBHOOK_SECRET"].encode()
    signature = request.headers.get("X-fsckmsft-Signature", "")

    expected = "sha256=" + hmac.new(
        secret, request.get_data(), hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        abort(401)

    event = request.get_json()
    print(f"Received event: {event['event']} ({event['id']})")
    return "", 200

Retries and delivery guarantees

fsckmsft delivers webhooks with at-least-once semantics. If your endpoint does not return a 2xx response within 10 seconds, fsckmsft marks the delivery as failed and retries automatically.
AttemptDelay after previous failure
1st retry30 seconds
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry8 hours
After five failed attempts, fsckmsft marks the event as permanently undelivered and stops retrying. You can inspect failed deliveries in Settings → Developer → Webhooks → [endpoint name] → Recent Deliveries and manually replay any individual event.
Because fsckmsft uses at-least-once delivery, your endpoint may occasionally receive the same event more than once (e.g., during a network timeout where your server processed the event but the 200 OK was not received). Always deduplicate on the id field before processing.

Manage webhook endpoints

From Settings → Developer → Webhooks, you can:
  • Edit an endpoint’s URL or event subscriptions at any time.
  • Rotate the signing secret — generates a new secret. Update your server’s environment variable promptly, as fsckmsft starts signing with the new secret immediately.
  • Pause delivery — temporarily stop sending events to an endpoint without deleting it. Useful during maintenance windows.
  • Delete — permanently removes the endpoint. Events generated after deletion are not queued.