Every screen of the Webhooks module, its intent, route, composition, states,
permissions, platform behavior and events. Authoritative components in
00-shared/03 ; this file enumerates which ones each screen uses with module
specifics.
States = idle / loading / success / empty / error(offline, rate, invalid) / disabled / permission.
Analytics events follow {module}.{screen}.{action} (proposed; SDK open - 00-shared/10 §8).
Field Detail
Purpose All tenant webhooks, newest first
Source GET /api/v1/webhooks (no pagination, sort createdAt: -1, webhooks.service.ts:35-37)
Widgets AppListTile per webhook: name, url (monospace, clipped), events chips (up to 3 + "+n"), trailing AppBadge (active success / paused neutral), AppMenu (Edit / Pause-Resume / Delete)
CTA / FAB AppFAB "New webhook" (webhook.create)
States loading (skeleton tiles), empty ("No webhooks yet"), error API, permission (webhook.read)
Row actions tap → detail (S3); delete → AppDialog confirm → DELETE /webhooks/:id (webhooks.controller.ts:45-50), optimistic remove, rollback on 404/500
Analytics webhooks.list.open, webhooks.list.create, webhooks.list.open_detail (proposed)
Field Detail
Purpose Register subscription: name, url, events, secret, enabled
Source POST /api/v1/webhooks (webhooks.controller.ts:21-25); DTO create-webhook.dto.ts:11-34
Inputs AppTextField name; url (.url keyboard); event picker (chips multi-select from event-queue-map.ts:6-43); secret (obscure + "generate" helper, min-length hint); enabled switch (default on, create-webhook.dto.ts:30-33)
Primary CTA "Create webhook"
Validation name/url/secret required; url @IsUrl({ require_tld: false }) (create-webhook.dto.ts:17-18); ≥1 event (ArrayMinSize(1), :22-23)
States loading, field errors, server error
Analytics webhooks.create.submit, webhooks.create.success, webhooks.create.failure (proposed)
Note Form is the largest surface - full spec in 08_Form_Specifications.md
Field Detail
Purpose Single subscription: config + health in one place
Source GET /api/v1/webhooks/:id (webhooks.controller.ts:33-37); metrics GET /webhooks/:id/metrics (:72-76, webhooks.service.ts:142-156)
Layout Header card (name, url, enabled badge, events chips, secret masked with reveal); metrics tile (total / success / failed / pending counts); recent delivery logs preview (5 rows); actions
Actions Edit (S4), Test (S7), Retry (S8, only when failed > 0), Pause/Resume (S9)
States loading skeleton, 404 → AppErrorState + back, error
Analytics webhooks.detail.open, webhooks.detail.tab.{config,logs} (proposed)
Field Detail
Purpose Modify url/events/secret/enabled - any subset
Source PATCH /api/v1/webhooks/:id (webhooks.controller.ts:39-43); UpdateWebhookDto = PartialType (all optional, update-webhook.dto.ts:4)
Inputs same as S2, pre-filled from detail doc; secret shown masked, blank = keep unchanged
Primary CTA "Save changes"
Note Secret rotation is a PATCH of secret today - a dedicated rotation flow is (forward-looking)
Field Detail
Purpose Per-attempt delivery history, newest first
Source GET /api/v1/webhooks/:id/logs - hard cap 50, sort createdAt: -1 (webhooks.service.ts:88-93)
Widgets AppListTile per log: eventType, status badge (pending amber / success green / failed red), responseCode, attemptedAt; tap → S6
Empty "No deliveries yet - trigger an event or use Test"
Pull-to-refresh RefreshIndicator re-fetches
States loading, error, empty
Analytics webhooks.logs.open, webhooks.logs.refresh (proposed)
Note One row per worker attempt (retries create new rows, webhooks.service.ts:62-67, 84-89); pagination (forward-looking)
Field Detail
Purpose Full attempt payload + response
Source same doc as S5 (payload, responseCode, responseBody, attemptedAt, completedAt - webhook-delivery-log.schema.ts:12-38)
Content eventType, status, timing, responseCode + responseBody (collapsed, monospace), full payload JSON (copyable)
Actions "Retry this webhook" (S8) if failed
a11y payload/code blocks exposed as selectable text, not image
Field Detail
Purpose Ping the endpoint with a synthetic event
Source POST /api/v1/webhooks/:id/test (webhooks.controller.ts:65-70) → queue job with eventType: 'WebhookTested', payload: { test: true, webhookId } (webhooks.service.ts:130-138)
Flow confirm dialog → success snackbar "Test delivery queued" (server returns { message: 'Test delivery queued' }, :69) → user watches S5
Note Async: dialog must not claim delivery, only enqueue (queue webhook-deliver, queue.constants.ts:14)
Field Detail
Purpose Re-enqueue the latest failed delivery
Source POST /api/v1/webhooks/:id/retry (webhooks.controller.ts:58-63) → 404 'No failed deliveries to retry' if none (webhooks.service.ts:103-108); job reuses the failed log's payload (:110-118)
Precondition latest log for this webhook has status: 'failed' (webhooks.service.ts:103-106)
Flow dialog "Retry latest failed delivery?" → snackbar "Retry queued" → S5 refresh
Note correlationId is sent as '' on retries (webhooks.service.ts:117)
Field Detail
Purpose Flip enabled without editing the record
Source POST /webhooks/:id/pause → setEnabled(false); POST /webhooks/:id/resume → setEnabled(true) (webhooks.controller.ts:78-90, webhooks.service.ts:158-161)
Behavior paused webhooks are skipped by fan-out (webhook.repository.ts:23-28); toggle back via resume
Note No pause vs disabled distinction in the model - single enabled boolean (webhook.schema.ts:22-23)
AppTextField, AppButton, AppSnackbar, AppCard, AppListTile, AppBottomSheet,
AppDialog, AppMenu, AppSkeleton, AppEmptyState, AppErrorState, AppOfflineBanner,
AppFAB, AppChips, AppBadge, AppSwitch, AppStatTile (metrics). Module-specific:
EventPickerChipField, SecretField, DeliveryLogTile, SignatureVerifyCard
(developer docs) - defined in 07_Component_Library.md .
webhooks.list.{open,create,open_detail}, webhooks.create.{submit,success,failure},
webhooks.detail.{open,pause,resume}, webhooks.edit.{submit,success},
webhooks.logs.{open,refresh,retry,test} (all proposed).
Forms portrait-first with resizeToAvoidBottomInset; tablet/desktop: list +
detail master-detail (S1 → S3), forms centered ≤ 480 dp.
Code blocks (url, payload, responseBody) full-width monospace with horizontal
scroll; long payloads collapse by default (S6).