Webhooks

Receive real-time event notifications from Sitemarks via HTTP webhooks.

Webhooks let your systems react to events in Sitemarks in real time. When something happens — an annotation is created, a comment is posted, a markup status changes — Sitemarks sends an HTTP POST request to the URL you configure.

Who can manage webhooks

Only organization owners and admins can create, update, or delete webhook registrations.


Supported events

| Event | Trigger | |-------|---------| | annotation.created | A new annotation is placed on a markup. | | annotation.resolved | An annotation is marked as resolved. | | comment.created | A new comment is added to an annotation. | | comment.updated | An existing comment is edited. | | markup.status_changed | A markup's status changes (for example, active to paused). | | markup.deleted | A markup is permanently deleted. |

When creating a webhook, you specify which events it subscribes to. A single webhook endpoint can listen to one event or all of them.


Payload format

Every webhook delivery is an HTTP POST with a JSON body containing three fields:

{
  "event": "comment.created",
  "payload": {
    "commentId": "cm1abc2def3gh",
    "annotationId": "cm4ijk5lmn6op",
    "markupId": "cm7qrs8tuv9wx",
    "projectId": "cm0yza1bcd2ef",
    "orgId": "cm3ghi4jkl5mn",
    "userId": "cm6opq7rst8uv",
    "userName": "Alice Chen",
    "body": "The logo needs more padding on the left.",
    "markupTitle": "Homepage v2"
  },
  "timestamp": "2026-03-22T14:30:00.000Z"
}

The shape of payload varies by event type. Refer to each event's specific fields:

  • annotation.createdannotationId, markupId, projectId, orgId, userId, userName, markupTitle, markupType, sourceUrl, pagePath
  • annotation.resolvedannotationId, markupId, projectId, orgId, userId, userName, markupTitle
  • comment.createdcommentId, annotationId, markupId, projectId, orgId, userId, userName, body, markupTitle
  • comment.updatedcommentId, annotationId, markupId, projectId, orgId, userId, body
  • markup.status_changedmarkupId, projectId, orgId, status, userId, userName, markupTitle
  • markup.deletedmarkupId, projectId, orgId, userId

Signature verification

Every webhook request includes an X-Webhook-Signature header that you can use to verify the request came from Sitemarks and was not altered in transit.

The signing secret is generated automatically when you create the webhook and is available from the webhook detail view in Settings.

Verification example (Node.js)

import crypto from "crypto";

function verifyWebhookSignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature, "hex"),
    Buffer.from(expected, "hex")
  );
}

// In your request handler:
const rawBody = req.body; // raw string, not parsed JSON
const signature = req.headers["x-webhook-signature"];
const secret = process.env.SITEMARKS_WEBHOOK_SECRET;

if (!verifyWebhookSignature(rawBody, signature, secret)) {
  return res.status(401).send("Invalid signature");
}

Use the raw body for verification

Verify the signature using the exact request body as received, not a re-serialized version. Character order and formatting matter for the signature to match.

The request also includes an X-Webhook-Event header with the event type string, which you can use for routing before parsing the body.


Retry behavior

Sitemarks considers a delivery successful if your endpoint responds with any 2xx status code within 5 seconds. If the request times out or returns a non-2xx status, the delivery is marked as failed.

Keep handlers fast

Do as little work as possible inside the webhook handler. Acknowledge the request with a 200 immediately, then process the payload asynchronously in a background job.


URL validation

Webhook URLs must be publicly accessible HTTPS endpoints. Private and internal network addresses are not supported.


Managing webhooks

Use the Settings > Integrations page to:

  • Create a webhook by providing a URL and selecting the events to subscribe to.
  • Update a webhook's URL or event subscriptions.
  • Delete a webhook when it is no longer needed. Deletion is immediate and irreversible.