---
title: "Delivery Detail Drawer"
description: "The portal's golden drawer: delivery state header, selectable attempt trail, and Monaco request/response inspectors in a non-modal right-hand sheet."
component: true
---

```tsx
"use client"

import { useState } from "react"
import { Button } from "@/components/ui/button"
import { DeliveryDetail, RowDetailSheet, type WebhookDeliverySummary } from "@/components/delivery-detail"
import type { WebhookAttempt } from "@/lib/webhook-types"

const delivery: WebhookDeliverySummary = {
  id: "del_9k2mf1",
  messageId: "msg_7h1q9x",
  endpointId: "ep_primary",
  type: "invoice.paid",
  generation: 0,
  replayOf: null,
  status: "success",
  failureReason: null,
  attemptCount: 3,
  nextRetryAt: null,
  lastStatusCode: 200,
  lastErrorClass: null,
  createdAt: "2026-07-30T11:47:00.000Z",
  completedAt: "2026-07-30T11:57:21.000Z",
  superseded: false,
  data: { id: "inv_2041", amountCents: 12900, currency: "EUR", paidAt: "2026-07-27T07:03:41.000Z" },
}

const attempts: WebhookAttempt[] = [
  {
    attempt: 1,
    generation: 0,
    responseStatus: null,
    latencyMs: null,
    errorClass: "timeout",
    url: "https://api.acme.dev/webhooks/repost",
    responseBody: null,
    requestHeaders: { "content-type": "application/json", "webhook-id": "msg_7h1q9x" },
    responseHeaders: null,
    timestamp: "2026-07-30T11:47:00.000Z",
  },
  {
    attempt: 2,
    generation: 0,
    responseStatus: 503,
    latencyMs: 1240,
    errorClass: "endpoint_5xx",
    url: "https://api.acme.dev/webhooks/repost",
    responseBody: "upstream unavailable",
    requestHeaders: { "content-type": "application/json", "webhook-id": "msg_7h1q9x" },
    responseHeaders: { "content-type": "text/plain" },
    timestamp: "2026-07-30T11:52:11.000Z",
  },
  {
    attempt: 3,
    generation: 0,
    responseStatus: 200,
    latencyMs: 84,
    errorClass: null,
    url: "https://api.acme.dev/webhooks/repost",
    responseBody: '{"ok":true}',
    requestHeaders: { "content-type": "application/json", "webhook-id": "msg_7h1q9x" },
    responseHeaders: { "content-type": "application/json" },
    timestamp: "2026-07-30T11:57:21.000Z",
  },
]

export function DeliveryDetailDemo() {
  const [open, setOpen] = useState(false)

  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        Open delivery details
      </Button>
      <RowDetailSheet open={open} onClose={() => setOpen(false)} title="Delivery details" description="Delivery attempt detail view">
        <DeliveryDetail
          deliveryId={delivery.id}
          delivery={delivery}
          attempts={attempts}
          isLoading={false}
          onReplay={() => setOpen(false)}
        />
      </RowDetailSheet>
    </>
  )
}

```

This is the drawer the Repost web app opens when you click a delivery, ported
whole. `RowDetailSheet` is the non-modal right-hand sheet (clicking another
table row swaps the detail in place instead of closing). `DeliveryDetail` is
the aggregate inside it: mutable delivery state up top, the immutable attempt
trail as a selectable table, and request/response inspectors for whichever
attempt is selected, with headers/body toggles and timestamp annotations.

## Installation

<ItemInstall name="delivery-detail" target="components/delivery-detail/delivery-detail.tsx" />

## Usage

Presentational: feed it from `useDeliveryAttempts` and wire `onReplay` to
`useReplayDelivery` ([@repost/portal-react](/docs/ui/portal-react)),
or any data of the same shape. The webhook-dashboard block composes it exactly
this way.

<ComponentSource name="delivery-detail-demo" title="components/delivery-detail-demo.tsx" />

## Props

| Prop | Type | Description |
| ---- | ---- | ----------- |
| `deliveryId` | `string` | The aggregate's id; also resets inspector state per delivery. |
| `delivery` | `WebhookDeliverySummary \| null` | Mutable state row; `null` renders the trail without state/actions (aged out). |
| `attempts` | `WebhookAttempt[]` | Ascending attempt trail. |
| `initialAttempt` | `{ generation, attempt }` | Pre-selects the clicked row's attempt. |
| `isLoading` / `isError` | `boolean` | Loading and error states for the trail. |
| `onReplay` / `replayPending` | — | Shows the Replay action; omit to hide (read-only portals). |
| `onRedeliver` / `redeliverPending` | — | Optional Redeliver action (hidden in the portal). |
