Webhooks · Pro plan

Webhooks Reference

Subscribe to RunTight events and receive a signed HTTP POST within about a minute of each one. Ideal for connecting to Zapier, Make, n8n, or your own integration services.

Setup

Go to Settings → Webhooks in your RunTight dashboard (admin role, Pro plan required). Click Add Webhook, enter a name and URL, optionally set a secret for HMAC verification, and check the events you want to receive.

Events

wo.created

A new work order is created — in the app, through the API, from an email or request, or from a PM schedule.

wo.completed

A work order's status changes to completed. Fires again if a reopened work order is completed again.

wo.overdue

An open or in-progress work order passes its due date, in your workspace's timezone. Fires once per WO.

pm.due

A work order is generated from a PM schedule — automatically or by a planner. A wo.created event is sent for it too.

asset.created

New equipment is added to your tenant.

Payload format

Every webhook delivers a JSON body with five top-level fields. id identifies the event and stays the same if it is retried; timestamp is when the event happened.

{
  "id": "event-uuid",
  "event": "wo.completed",
  "tenant_id": "tenant-uuid",
  "timestamp": "2026-04-11T18:45:12.123Z",
  "data": {
    "work_order_id": "wo-uuid",
    "title": "Weekly Hydraulic Press Inspection",
    "priority": "high",
    "due_date": "2026-04-11",
    "completed_at": "2026-04-11T18:45:10.567Z",
    "time_spent_minutes": 35,
    "asset_id": "asset-uuid"
  }
}

The data field varies by event type:

  • wo.created: work_order_id, title, priority, status, wo_type, due_date, asset_id
  • wo.completed: work_order_id, title, priority, due_date, completed_at, time_spent_minutes, asset_id
  • wo.overdue: work_order_id, title, priority, due_date, asset_id
  • pm.due: work_order_id, template_id, title, priority, due_date, asset_id
  • asset.created: asset_id, name, category, criticality

Request headers

Content-Type: application/json
User-Agent: RunTight-Webhooks/1.0
X-Webhook-Event: wo.completed
X-Webhook-Id: event-uuid                  (same as the body's id)
X-Webhook-Signature: sha256=HEX_HMAC      (only if secret configured)

Signature verification

If you configure a secret on a webhook, every request includes an X-Webhook-Signature header containing an HMAC-SHA256 hex digest of the raw request body.

Node.js example

import crypto from "crypto";

function verifySignature(body, signatureHeader, secret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  );
}

// In your webhook handler:
const rawBody = await request.text();
const signature = request.headers.get("x-webhook-signature");
if (!verifySignature(rawBody, signature, process.env.WEBHOOK_SECRET)) {
  return new Response("Invalid signature", { status: 401 });
}
const payload = JSON.parse(rawBody);

Delivery semantics

  • Timing: Events are queued the moment they happen and delivered within about a minute. A webhook receives events that happen after it is created.
  • Retries: If your endpoint does not return a 2xx, RunTight tries again after 1, 5 and 15 minutes, then 1, 3, 6 and 12 hours — up to 8 attempts within 24 hours — before marking the event failed. While an endpoint is failing, its other events wait rather than piling on.
  • At least once: Rarely, an event can arrive twice. Use the event's id to ignore duplicates.
  • Order: Events for an endpoint are sent oldest first. An event that had to be retried can arrive after newer ones, so compare timestamp if order matters.
  • Timeout: RunTight waits up to 10 seconds for your endpoint to respond. Return a 2xx quickly and process asynchronously if you need more time.
  • History: Every delivery attempt is logged and visible under Settings → Webhooks with status code and response body snippet. Use this to debug.
  • Pause: You can pause a webhook without deleting it. Events that happen while it is paused are not sent later, and events still waiting when you pause it are dropped.

Common integrations

  • Zapier — create a “Catch Hook” trigger, paste the Webhook URL into RunTight. Chain into Slack, Google Sheets, Trello, ClickUp, etc.
  • Make (formerly Integromat) — add a “Custom webhook” module, use it as a trigger.
  • n8n — add a Webhook node, select POST method. Self-host for zero ongoing cost.
  • Your own code — any HTTPS endpoint that accepts POST with JSON body will work.

See also