Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.useautumn.com/llms.txt

Use this file to discover all available pages before exploring further.

Forward Autumn webhook events to a Slack or Discord channel with ready-made Svix transformations that turn each event into a clean, formatted message.

Supported events

EventSlackDiscord
customer.products.updated
balances.limit_reached
balances.usage_alert_triggered
Other event types — including Vercel Marketplace events — are skipped by the transforms so they are never delivered to your Slack or Discord channel.

Setup

1

Get an incoming webhook URL

Follow the official guide for the platform you want to use:Both flows give you a webhook URL that looks like https://hooks.slack.com/services/... or https://discord.com/api/webhooks/.... Keep it handy for the next step.
2

Add the webhook endpoint in Autumn

In your Autumn dashboard, go to Developer → Webhooks and click Add Endpoint. Paste in the URL from the previous step and select the events you want to subscribe to.
Adding a webhook endpoint in the Autumn dashboard
3

Enable transformations

Open the endpoint you just created, switch to the Advanced tab, and click Edit transformation.
The Advanced tab on a webhook endpoint, showing the Edit transformation button
Paste in the transform code below for the platform you’re targeting, then click Save and Enable.
The transformation editor with the Enabled toggle, code area, and Save and Enable button highlighted
4

Enable throttling

Back on the Advanced tab, click Edit next to Endpoint Throttling and set a sensible RPS (requests-per-second) limit. This prevents your Slack or Discord channel from being flooded during high-volume events such as bulk customer migrations or backfills.
Endpoint throttling editor with an RPS input field

Transform code

Copy the transform for your platform into the Code editor on the transformation page, then click Save and Enable.
/**
 * @param webhook the webhook object
 * @param webhook.method destination method. Allowed values: "POST", "PUT"
 * @param webhook.url current destination address
 * @param webhook.eventType current webhook Event Type
 * @param webhook.payload JSON payload
 * @param webhook.cancel whether to cancel dispatch of the given webhook
 */
function handler(webhook) {
  var AUTUMN_BASE = "https://app.useautumn.com/customers/";
  var AUTUMN_USERNAME = "Autumn";
  var AUTUMN_ICON_URL = "https://i.ibb.co/BHCF1ZqL/autumnicon.png";

  var payload = webhook.payload || {};
  var data = payload.data || payload;

  // ============ customer.products.updated ============
  if (webhook.eventType === "customer.products.updated") {
    var scenario = data.scenario || "updated";
    var customer = data.customer || {};
    var entity = data.entity || null;
    var product = data.updated_product || {};

    var customerName = customer.name || customer.email || customer.id || "Customer";
    var customerEmail = customer.email || null;
    var customerId = customer.id || "";

    var productName = product.name || "their plan";
    if (product.version && product.version !== 1) {
      productName = productName + " V" + product.version;
    }

    var entityLabel = null;
    if (entity) {
      entityLabel = entity.name || entity.id || null;
    }

    var meta = {
      "new":        { emoji: "🎉", header: "New Subscription",         verb: "subscribed to" },
      "upgrade":    { emoji: "🚀", header: "Customer Upgraded",        verb: "upgraded to" },
      "downgrade":  { emoji: "📉", header: "Customer Downgraded",      verb: "downgraded to" },
      "cancel":     { emoji: "⚠️",  header: "Subscription Cancelled",   verb: "cancelled" },
      "renew":      { emoji: "🔄", header: "Subscription Uncancelled", verb: "uncancelled" },
      "expired":    { emoji: "💀", header: "Subscription Expired",     verb: "expired on" },
      "scheduled":  { emoji: "📅", header: "Change Scheduled",         verb: "scheduled a change to" }
    }[scenario] || { emoji: "🔔", header: "Subscription Updated", verb: "updated" };

    var sentence;
    if (scenario === "expired") {
      sentence = "*" + customerName + "*'s *" + productName + "* expired";
    } else {
      sentence = "*" + customerName + "* " + meta.verb + " *" + productName + "*";
    }

    var previewText = meta.emoji + " " + customerName + " " + meta.verb + " " + productName;

    var fields = [
      { type: "mrkdwn", text: "*Customer:*\n" + customerName }
    ];
    if (customerEmail) {
      fields.push({ type: "mrkdwn", text: "*Email:*\n" + customerEmail });
    }
    fields.push({ type: "mrkdwn", text: "*Product:*\n" + productName });
    fields.push({ type: "mrkdwn", text: "*Scenario:*\n`" + scenario + "`" });
    if (entityLabel) {
      fields.push({ type: "mrkdwn", text: "*Entity:*\n" + entityLabel });
    }

    var contextParts = [];
    if (customerId) {
      contextParts.push("Customer ID: `" + customerId + "`");
    }
    if (entity && entity.id) {
      contextParts.push("Entity: `" + entity.id + "`");
    }

    var blocks = [
      {
        type: "header",
        text: { type: "plain_text", text: meta.emoji + " " + meta.header, emoji: true }
      },
      {
        type: "section",
        text: { type: "mrkdwn", text: sentence }
      },
      {
        type: "section",
        fields: fields
      }
    ];

    if (customerId) {
      blocks.push({
        type: "actions",
        elements: [
          {
            type: "button",
            text: { type: "plain_text", text: "View in Autumn", emoji: true },
            url: AUTUMN_BASE + customerId,
            style: "primary"
          }
        ]
      });
    }

    if (contextParts.length > 0) {
      blocks.push({
        type: "context",
        elements: [
          { type: "mrkdwn", text: contextParts.join(" | ") }
        ]
      });
    }

    webhook.payload = {
      username: AUTUMN_USERNAME,
      icon_url: AUTUMN_ICON_URL,
      text: previewText,
      blocks: blocks
    };
    return webhook;
  }

  // ============ balances.limit_reached ============
  if (webhook.eventType === "balances.limit_reached") {
    var lrCustomerId = data.customer_id || "";
    var lrFeatureId = data.feature_id || "feature";
    var lrLimitType = data.limit_type || "included";
    var lrEntityId = data.entity_id || null;

    var lrCustomerLink = lrCustomerId
      ? "<" + AUTUMN_BASE + lrCustomerId + "|`" + lrCustomerId + "`>"
      : "`unknown`";

    var lrSentence =
      lrCustomerLink + " hit their *" + lrFeatureId + "* `" + lrLimitType + "` limit";

    var lrPreview = "🚫 " + lrCustomerId + " hit their " + lrFeatureId + " limit";

    var lrFields = [
      { type: "mrkdwn", text: "*Customer:*\n" + lrCustomerLink },
      { type: "mrkdwn", text: "*Feature:*\n`" + lrFeatureId + "`" },
      { type: "mrkdwn", text: "*Limit Type:*\n`" + lrLimitType + "`" }
    ];
    if (lrEntityId) {
      lrFields.push({ type: "mrkdwn", text: "*Entity:*\n`" + lrEntityId + "`" });
    }

    var lrContextParts = [];
    if (lrCustomerId) lrContextParts.push("Customer ID: `" + lrCustomerId + "`");
    if (lrEntityId) lrContextParts.push("Entity: `" + lrEntityId + "`");

    var lrBlocks = [
      {
        type: "header",
        text: { type: "plain_text", text: "🚫 Limit Reached", emoji: true }
      },
      {
        type: "section",
        text: { type: "mrkdwn", text: lrSentence }
      },
      {
        type: "section",
        fields: lrFields
      }
    ];

    if (lrCustomerId) {
      lrBlocks.push({
        type: "actions",
        elements: [
          {
            type: "button",
            text: { type: "plain_text", text: "View in Autumn", emoji: true },
            url: AUTUMN_BASE + lrCustomerId,
            style: "danger"
          }
        ]
      });
    }

    if (lrContextParts.length > 0) {
      lrBlocks.push({
        type: "context",
        elements: [
          { type: "mrkdwn", text: lrContextParts.join(" | ") }
        ]
      });
    }

    webhook.payload = {
      username: AUTUMN_USERNAME,
      icon_url: AUTUMN_ICON_URL,
      text: lrPreview,
      blocks: lrBlocks
    };
    return webhook;
  }

  // ============ balances.usage_alert_triggered ============
  if (webhook.eventType === "balances.usage_alert_triggered") {
    var uaCustomerId = data.customer_id || "";
    var uaFeatureId = data.feature_id || "feature";
    var uaEntityId = data.entity_id || null;
    var uaAlert = data.usage_alert || {};
    var uaAlertName = uaAlert.name || "Usage alert";
    var uaThreshold = uaAlert.threshold;
    var uaThresholdType = uaAlert.threshold_type || "usage";

    function formatThreshold(value, type) {
      if (value === undefined || value === null) return "—";
      if (type === "usage_percentage") return value + "% used";
      if (type === "remaining_percentage") return value + "% remaining";
      if (type === "remaining") return value + " remaining";
      return value + " used";
    }

    var uaThresholdLabel = formatThreshold(uaThreshold, uaThresholdType);

    var uaCustomerLink = uaCustomerId
      ? "<" + AUTUMN_BASE + uaCustomerId + "|`" + uaCustomerId + "`>"
      : "`unknown`";

    var uaSentence =
      uaCustomerLink + " crossed the *" + uaAlertName + "* threshold on *" + uaFeatureId + "*";

    var uaPreview = "📊 Usage Alert: " + uaAlertName + " (" + uaCustomerId + ")";

    var uaFields = [
      { type: "mrkdwn", text: "*Customer:*\n" + uaCustomerLink },
      { type: "mrkdwn", text: "*Feature:*\n`" + uaFeatureId + "`" },
      { type: "mrkdwn", text: "*Alert:*\n" + uaAlertName },
      { type: "mrkdwn", text: "*Threshold:*\n" + uaThresholdLabel }
    ];
    if (uaEntityId) {
      uaFields.push({ type: "mrkdwn", text: "*Entity:*\n`" + uaEntityId + "`" });
    }

    var uaContextParts = [];
    if (uaCustomerId) uaContextParts.push("Customer ID: `" + uaCustomerId + "`");
    if (uaEntityId) uaContextParts.push("Entity: `" + uaEntityId + "`");

    var uaBlocks = [
      {
        type: "header",
        text: { type: "plain_text", text: "📊 Usage Alert: " + uaAlertName, emoji: true }
      },
      {
        type: "section",
        text: { type: "mrkdwn", text: uaSentence }
      },
      {
        type: "section",
        fields: uaFields
      }
    ];

    if (uaCustomerId) {
      uaBlocks.push({
        type: "actions",
        elements: [
          {
            type: "button",
            text: { type: "plain_text", text: "View in Autumn", emoji: true },
            url: AUTUMN_BASE + uaCustomerId
          }
        ]
      });
    }

    if (uaContextParts.length > 0) {
      uaBlocks.push({
        type: "context",
        elements: [
          { type: "mrkdwn", text: uaContextParts.join(" | ") }
        ]
      });
    }

    webhook.payload = {
      username: AUTUMN_USERNAME,
      icon_url: AUTUMN_ICON_URL,
      text: uaPreview,
      blocks: uaBlocks
    };
    return webhook;
  }

  // Cancel any other event types — they don't match Slack's expected schema and would error.
  webhook.cancel = true;
  return webhook;
}