Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

08 - Form Specifications (Webhooks Module)

The webhook form (create + edit share one model). Exact DTO contracts quoted from create-webhook.dto.ts / update-webhook.dto.ts; validation mirrors class-validator rules so client and server agree.


1. Webhook form (S2 create / S4 edit)

FieldControlRequiredRules (server = source)Server source
nameAppTextFieldyes (create) / optional (edit)non-empty string@IsString, create-webhook.dto.ts:13-14
urlAppTextField (.url keyboard)yes / optionalvalid URL; require_tld: false (local/private hosts allowed)@IsUrl({ require_tld: false }), :17-18
eventsEventPickerChipFieldyes (≥ 1)array of strings, min size 1@IsArray + @ArrayMinSize(1) + @IsString({ each: true }), :20-24
secretSecretFieldyes (create) / blank = keep (edit)non-empty string@IsString, :27-28
enabledAppSwitchnoboolean, default true@IsOptional + @IsBoolean, :30-33; schema default webhook.schema.ts:22-23

Edit mode: UpdateWebhookDto = PartialType(CreateWebhookDto) - every field optional (update-webhook.dto.ts:4); client sends only dirty fields.

2. Wire payloads

// POST /api/v1/webhooks  (create-webhook.dto.ts:11-34)
{
  "name": "SMS Gateway",
  "url": "https://vendor.example.com/hooks/sms",
  "events": ["AttendanceMarked", "HomeworkSubmitted"],
  "secret": "whsec_9f2c…",
  "enabled": true
}

// PATCH /api/v1/webhooks/:id  (update-webhook.dto.ts:4)
{ "url": "https://vendor.example.com/hooks/sms/v2", "enabled": false }

3. Event picker vocabulary (client-side)

The API accepts any string, so the picker offers the emitted event registry (event-queue-map.ts:6-43) grouped by module, plus WebhookTested (webhooks.service.ts:134):

GroupEvents (event-queue-map.ts)
Auth / usersUserRegistered, UserLoggedIn, PasswordResetRequested, UserCreated, UserUpdated, UserDeleted (:7-12)
OrganizationOrganizationCreated (:13)
AttendanceAttendanceMarked, AttendanceUpdated (:14-21)
HomeworkHomeworkCreated, HomeworkUpdated, HomeworkSubmitted, HomeworkGraded, HomeworkDeleted (:22-26)
ResultsExamResultsPublished (:27)
PeopleStudentCreated/Updated/Deleted, TeacherCreated/Updated/Deleted, StaffCreated/Updated/Deleted, ParentCreated/Updated/Deleted (:28-39)
Fees / paymentsFeeStructureCreated, InvoiceIssued, PaymentCompleted (:40-42)
TestWebhookTested (test ping, webhooks.service.ts:134)

"Custom event" input (optional, allowCustom): free text - matching is exact string equality (webhook.repository.ts:27), typos silently never fire.

4. Client-side validation order

  1. name empty → "Name is required".
  2. url not parseable / no scheme → "Enter a valid URL" (mirror @IsUrl, create-webhook.dto.ts:17-18).
  3. events empty → "Select at least one event" (mirror @ArrayMinSize(1), :22-23).
  4. secret empty (create) → "Secret is required" (mirror @IsString, :27-28).
  5. enabled boolean coercion (switch) - no text validation.
  6. Server 400s: map class-validator message arrays to fields; unknown messages → top-of-form error.

5. Submit behaviour

  • Create: POST /api/v1/webhooks (webhooks.controller.ts:21-25) → on success pop to detail (S3); on 400 keep form + field errors; on 401/500 AppErrorState.
  • Edit: PATCH /api/v1/webhooks/:id (webhooks.controller.ts:39-43) → same, pop to S3 with refreshed doc; 404 → error state (webhook deleted elsewhere).
  • Both: submitting lock (CTA spinner, fields disabled); no double submit.

6. Secret handling

  • Create: required; "Generate" helper fills a strong random value client-side.
  • Edit: blank = keep unchanged (never echo the stored secret into the field; show placeholder "Leave blank to keep current secret").
  • Detail (S3): masked with reveal; copy-to-clipboard.
  • Security: secret is stored and returned in plaintext by the API (webhooks.service.ts:31-47); rotation = PATCH secret. Dedicated rotation flow with verification event (forward-looking).

7. Pause / Resume (non-form action)

  • No form: POST /webhooks/:id/pausesetEnabled(false), POST /webhooks/:id/resumesetEnabled(true) (webhooks.controller.ts:78-90; webhooks.service.ts:158-161).
  • Paused ≠ deleted: fan-out filter drops it (webhook.repository.ts:23-28).