Send Kelvin's events to another system with webhooks

Subscribe a web address to a client organisation's events, check each delivery's signature, and pause or remove a subscription.

Working with Deployr · For owners and admins · Updated 7 Oct 2026

A webhook subscription sends a client organisation's events to a web address you choose, as each one happens.

  1. 1

    Under Outbound webhooks, choose the Organisation whose events to send.

  2. 2

    Enter the Target URL. It must be https, to a named host: never http, an IP address or a local name.

  3. 3

    Tick the Events to send, and choose Add subscription.

  4. 4

    Kelvin shows the subscription's signing secret once. Choose Copy and put it in the system receiving the deliveries, which checks every delivery with it.

The secret leaves the page a few seconds after it is copied, when the browser tab is hidden, or after 10 minutes. Kelvin keeps it to sign deliveries, but can't show it again. If it wasn't copied, add the subscription again for a new secret, then remove the old one.

The subscription is listed with its organisation, its events, its Failures and its Status: Active or Paused.

What is sent

Each event is posted to the address as JSON: the event's name, when it was sent, and the request or project it is about. Kelvin sends four events, all for changes made through the Kelvin API, by a client's Deployr or another system holding one of your firm's keys:

  • engineering_request.created and engineering_request.updated, when a request is raised or changed.
  • engineering_project.created, when a project is created.
  • engineering.change, when a project's status is changed.

These are the only events offered. An older subscription may also name events Kelvin used to offer but never sent; its row lists them as "Also ticked, never sent".

Each delivery carries four headers:

  • X-Kelvin-Event: the event's name.
  • X-Kelvin-Delivery: the delivery's id, the same on every attempt.
  • X-Kelvin-Timestamp: when the attempt was sent, in whole seconds since 1 January 1970 UTC.
  • X-Kelvin-Signature: sha256= and the delivery's signature in lowercase hex.

Check that a delivery came from Kelvin

The signature is an HMAC-SHA256 made with the subscription's signing secret. Anyone can post to your address; only Kelvin holds the secret. On every delivery:

  1. 1

    Read the request body exactly as it arrived, before parsing it. The signature is over those bytes, and parsing and re-encoding the JSON changes them.

  2. 2

    Join the X-Kelvin-Timestamp value, a full stop and the body, and compute the HMAC-SHA256 of that, keyed with the whole signing secret as text, whsec_ included.

  3. 3

    Write the result in lowercase hex after sha256=, and compare it with X-Kelvin-Signature using a constant-time comparison. If they differ, refuse the delivery and don't act on it.

  4. 4

    Refuse it too if X-Kelvin-Timestamp is more than 5 minutes from your own clock. The timestamp is part of what is signed, so this stops an old delivery being sent again.

  5. 5

    Use X-Kelvin-Delivery to skip a delivery you have already handled: a retry carries the same id.

Pausing and removing

  • Choose Pause on a subscription's row to stop sending to it. Its status reads Paused, and nothing is sent to it until you choose Resume.
  • Choose Remove and confirm with Remove subscription to delete it. Nothing more is sent to it, and its past deliveries stay in Recent deliveries.
  • A subscription's address and events can't be edited. Add a new subscription with the ones you want, then remove the old one.

Deliveries

  • Kelvin tries a delivery up to 3 times. An attempt with no answer within 10 seconds fails, and a redirect isn't followed.
  • Each attempt is signed afresh, with its own timestamp.
  • Recent deliveries lists the newest 20, refreshed every 30 seconds, each Delivered or Failed with the response code, its attempts and when it was sent.
  • Failures on a subscription counts its failed deliveries since the last one that went through.
  • Failed deliveries also appear in the event margin.

Note

Events that happen while a subscription is paused aren't sent to it later.