Skip to main content
Onset provides webhooks which allow you to receive HTTP push notifications whenever data is created, updated or removed. This allows you to build integrations that respond to changes in real time, such as triggering CI builds, updating external systems, or sending messages based on release activity.

Payload

The webhook HTTP payload will include information both in its HTTP headers and its request body.
Onset-Signaturex
string
HMAC signature of the webhook payload.
Onset-Event
string
Event which triggered this webhook request.
Onset-Workspace
string
Onset workspace Id.

Body

event
string
Event which triggered this webhook request.
actor
string
The actor who triggered the action. Could be a User, Visitor, or API.
workspaceId
string
Workspace ID
webhookId
string
Webhook ID
webhookTimestamp
string
UNIX timestamp when the webhook was sent.
data
string
Payload of the entity

Securing Webhooks

You should ensure that webhooks you received were sent by Onset. You can do this by verifying the webhook request signature and timestamp. Onset sends a Onset-Signature HTTP header with every webhook request. This header contains a hex-encoded HMAC-SHA256 signature of the raw body contents, signed using the webhook’s signing secret. You can find the signing secret on the webhook’s detail page. The parsed JSON body has a webhookTimestamp field with a UNIX timestamp, in milliseconds, indicating the time when the webhook was sent. We recommend that you verify it’s within a minute of the time your system sees it to guard against replay attacks. To verify the webhook, you need to compute the signature of the request body using the webhook’s signing secret and compare it against the Onset-Signature header. It’s strongly recommended to use raw request body rather than restringifying a parsed JSON body, otherwise the signature may differ. Once the signature has been validated, check to ensure that the webhook timestamp is reasonably current before processing the request:
// Express example:
const crypto = require('node:crypto');
const express = require('express');

const ONSET_WEBHOOK_SECRET = process.env.ONSET_WEBHOOK_SECRET;

function verifySignature(headerSignatureString, rawBody) {
  if (typeof headerSignatureString !== 'string') {
    return false;
  }
  const headerSignature = Buffer.from(headerSignatureString, 'hex');
  const computedSignature = crypto
    .createHmac('sha256', ONSET_WEBHOOK_SECRET)
    .update(rawBody)
    .digest();

  return crypto.timingSafeEqual(computedSignature, headerSignature);
}

const app = express();

app.post(
  '/webhook',
  express.json({
    verify: (req, _res, buf) => {
      // Capture the raw body for signature verification.
      req.rawBody = buf;
    },
  }),
  (req, res) => {
    if (!verifySignature(req.get('onset-signature'), req.rawBody)) {
      return res.sendStatus(401);
    }

    if (Math.abs(Date.now() - req.body.webhookTimestamp) > 60 * 1000) {
      // Reject any webhooks not within 60 seconds of the current time to prevent replay attacks.
      return res.sendStatus(401);
    }

    try {
      // ... Handle verified webhook ...
      return res.sendStatus(200);
    } catch (err) {
      // Indicate to Linear that there was a server error so the webhook is retried later.
      return res.sendStatus(500);
    }
  },
);

app.listen(8080, () => console.log('Serving on port 8080'));