Saku

Outbound webhooks Max

Send HTTPS POST requests to your own endpoints when cards change or comments are added. Webhooks are configured per board under board settings.

Overview

Each webhook has a URL, a list of subscribed events, and either a simple checkbox-driven JSON body or an advanced template you define. Deliveries appear in the board's recent delivery log; failed requests are not retried automatically.

Availability

Outbound webhooks are included on the Max plan only.

Simple vs advanced payload

Simple mode builds a standard envelope: version, event, occurred_at, and optional sections (board, list, card, labels, assignees, and so on) according to the toggles you enable.

Advanced mode lets you supply a JSON template. Tokens look like {card_title} and must sit where a JSON value is valid, for example "title": {card_title}, not inside a JSON string. Each token is replaced with the correct JSON (string, number, object, array, or null), then the result is parsed. Invalid JSON after substitution falls back to the simple envelope for that delivery (and a warning is logged).

In advanced mode, the same toggles as Simple still control which data is resolved: if you disable labels, {labels} will be an empty array.

Request format and signing

Every delivery is an HTTP POST with Content-Type: application/json and a JSON body (either the simple envelope or your advanced template result).

  • X-Saku-Event holds the event name (e.g. card.created).
  • X-Saku-Delivery holds a unique id for that attempt.
  • X-Saku-Signature when a signing secret is set contains sha256=<hex>, HMAC-SHA256 over the raw request body.

Template variables

Names are case-insensitive. Dots in a token are treated like underscores (e.g. {board.name} → board_name). Common aliases include {cardtitle} for {card_title}.

PlaceholderMeaning
{version}Payload schema version (currently 2)
{event}Event id (e.g. card.created)
{occurred_at}ISO-8601 timestamp when the webhook was sent
{board_id}Board id
{board_name}Board name
{list_id}List id for the card
{list_name}List name
{card_id}Card id
{card_title}Card title
{card_description}Card description (string or null)
{card_priority}Priority enum string
{card_due_date}Due date ISO string or null
{card_archived}Whether the card is archived
{card_position}Numeric position in list
{card_list_id}List id the card belongs to
{card_created_at}ISO created timestamp
{card_updated_at}ISO updated timestamp
{card_url}Deep link to the card in Saku (null if no card)
{actor_id}User id who triggered the action
{actor_name}Display name or null
{changes}Array of change objects (card.updated); empty if none
{labels}Array of {id, name, colour}
{assignees}Array of {user_id, name}
{custom_fields}Array of {field_id, name, value}
{move}Object {from: {id, name}, to: {id, name}} or null
{label}Object {id, name, colour} for label events; null otherwise
{assignee}Object {user_id, name} for assignee events; null otherwise
{comment}Object {id, content, parent_comment_id, created_at} or null
{deleted_card}Object {id, title, list_id, board_id} after delete; null otherwise
{test_message}Only for webhook.test: human-readable test notice

Nested objects from the simple envelope (e.g. {board}, {card}) are also available when those sections are included.

Examples

Minimal custom shape for a Slack-style incoming webhook or Zapier catch:

{
  "source": "saku",
  "event": {event},
  "board": {board_name},
  "card": {card_title},
  "url": {card_url}
}

Test deliveries (webhook.test) expose {test_message} and {message} with the same text as the default test body; other card fields are typically null or empty.

Delivery log

The webhook tab shows recent attempts with HTTP status, duration, and a short response preview. Use it after changing your endpoint or template, or after triggering events on the board.

The brain for your business

Saku is a product of Florian Industries, which is registered with the Companies Registration Office in Ireland (797293). © 2026 Saku.