Skip to content
TuffTasks, docs and chat for teams and AI agents

Observability

Delivery Heatmap

Webhook attempts by endpoint and hour — volume as bar height, failing hours picked out, with a dot-column total on top.

1 variantMIT

Preview

Webhooks: Deliveries

Attempts by endpoint

Nov 4 · UTC

orders-api
billing-svc
crm-sync
slack-relay
analytics
fulfil-eu
search-index
audit-log
FewerMore
Failing hour
Attempts
28,825
Delivered
28,721
Failed
104

Attempts by endpoint, Nov 4 · UTC: 8 endpoints over 24 columns, 28,825 attempts, 104 failed. Failing: slack-relay at 19:00, audit-log at 03:00.

Installation

Copies the component into components/ui and installs its dependencies and theme tokens.

pnpm dlx shadcn@latest add https://tuffui.com/r/delivery-heatmap.json

Usage

Pass your own data and wire the callbacks to your agent runtime.

import { DeliveryHeatmap, type DeliveryRange } from "@/components/ui/delivery-heatmap";

export function WebhookDeliveries({ day }: { day: string }) {
  const [range, setRange] = useState<DeliveryRange>("24h");
  // One row per endpoint, one bucket per 5 min / hour / day.
  const { endpoints, labels } = useDeliveryStats(range);

  return (
    <DeliveryHeatmap
      range={range}
      onRangeChange={setRange}
      date={`${day} · UTC`}
      labels={labels} // ["00:00", "01:00", …]
      endpoints={endpoints.map((e) => ({
        id: e.id,
        label: e.name, // "billing-svc"
        buckets: e.buckets.map((b) => ({
          attempts: b.attempts, // retries included
          failed: b.failed,
          status: b.topErrorStatus, // 503
        })),
      }))}
      onSelect={(endpoint, i) => openAttempts(endpoint.id, labels[i])}
    />
  );
}

Props

PropTypeDefaultDescription
endpoints*DeliveryEndpoint[]—One row per endpoint, top to bottom. Each bucket fills its slot as high as its attempt volume, on five levels against the busiest bucket in view; a failing bucket turns red.
labels*string[]—One label per column, e.g. `"11:00"` or `"Nov 4"`. Sets the column count and is used on the axis and in the tooltip.
ticksnumber[]even spreadColumn indexes to label on the axis. Left out, labels thin out to fit the width.
range"1h" | "24h" | "7d"—Controlled range. The component doesn't bucket anything itself: swap `endpoints` and `labels` when it changes, and the cells refill with a short stagger.
defaultRange"1h" | "24h" | "7d""24h"Starting range when uncontrolled.
onRangeChange(range: DeliveryRange) => void—Shows the 1h / 24h / 7d switch. Called with the new range.
failThresholdnumber0.05Share of failed attempts from which a bucket reads as failing. A bucket's own `failing` flag wins over it.
titlestring"Deliveries"Header title.
sectionstring"Webhooks"Breadcrumb before the title. Pass an empty string to drop it.
subtitlestring—Secondary line under the title.
headingstring"Attempts by endpoint"Heading above the chart, also the grid's accessible name.
datestring—Window shown next to the heading, e.g. "Nov 4 · UTC".
statsDeliveryStat[]attempts, delivered, failedFooter row. Numbers roll to their new value. Pass `[]` to hide it.
introbooleantrueFills the cells in column by column on mount. Off renders the finished chart. Skipped with reduced motion.
formatCount(n: number) => string"61,204"Formats counts in the tooltip, footer and cell labels.
onSelect(endpoint: DeliveryEndpoint, bucketIndex: number) => void—Called when a cell is clicked, or picked with Enter. Cells take arrow keys, Home and End; hover and focus show the tooltip on their own.
classNamestring—Extra classes for the root element.
type DeliveryRange = "1h" | "24h" | "7d";

type DeliveryEndpoint = {
  id: string;
  label: string;             // "billing-svc"
  buckets: DeliveryBucket[]; // same order as labels
};

type DeliveryBucket = {
  attempts: number;          // retries included
  failed: number;
  status?: number | string;  // shown in the tooltip, e.g. 503
  failing?: boolean;         // overrides failThreshold
};

type DeliveryStat = {
  label: string;
  value: number | string;
  tone?: "neutral" | "brand" | "negative";
};