Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
129 KiB
129 KiB
Scratchpad — Inbound Webhooks
- Plan slug:
2026-05-11-inbound-webhooks - Created:
2026-05-11
What This Is
Working memory for inbound webhook implementation. Capture discoveries, decisions, and gotchas as we go.
Decisions
- (2026-05-11) No source presets in v1. Ship generic configurable webhooks only. Per-source presets deferred to v2 once we see actual payload shapes in production.
- (2026-05-11) Action registry pattern, not hardcoded action list. Core registry in
server/src/lib/inboundWebhooks/actions/registry.ts; each@alga-psa/*package contributes via its owninboundActions.ts. v1 ships 13 actions across 8 entity types: tickets (4), clients (2), contacts (1), assets (1 with RMM/non-RMM branches), invoices (2), time entries (1), project tasks (2), cross-cutting tag (1). - (2026-05-11) External-ID lookup uses existing
tenant_external_entity_mappings. No newexternal_refcolumns are added to entity tables.integration_typeis set to the webhook slug to namespace mappings per user-defined webhook. - (2026-05-11) Asset upsert reuses
ingestNormalizedRmmDeviceSnapshotwhen payload is RMM-shaped — same pipeline used by Tanium, NinjaOne, Tactical RMM. Generic asset upsert path for non-device payloads. - (2026-05-11) Reuse workflow editor's JSONata expression authoring (
ExpressionTextArea,useExpressionAutocomplete, expression-authoring path discovery). New context adapter introspects the captured sample payload. This avoids building a separate mapping UI. - (2026-05-11) Workflow handler receives a normalized envelope + raw body. Envelope:
{source, body, headers, verified, delivery_id, idempotency_key, received_at}. Since v1 has no per-source normalizers, the envelope is "identity normalization" — body is the parsed JSON as received. - (2026-05-11) Settings page becomes tabbed (Inbound | Outbound). Existing
AdminWebhooksSetup.tsx(outbound) gets factored into the Outbound tab; no behavior changes to outbound. - (2026-05-11) Feature flag
inbound_webhooks_enabled(PostHog) gates both the Settings tab and the/api/inbound/*route. Default off; enable per-tenant for early adopters. - (2026-05-11) Replay re-evaluates with CURRENT config. Simpler than snapshotting mapping per delivery. Document this in the UI so users know what to expect.
- (2026-05-11) Bundled integrations stay as-is in v1. NinjaOne / Tactical RMM / Tanium / Xero / QBO / Entra continue to use their existing code paths. Consolidation under the generic system is a v2+ candidate.
Discoveries / Constraints
- (2026-05-11) F001 implemented in
server/migrations/20260511100000_create_inbound_webhooks_table.cjs. The table uses composite PK(tenant, inbound_webhook_id), unique(tenant, slug), Citus distribution bytenant, and keeps auth secrets indirect throughauth_config(vault path/config metadata). Addedsample_capture_expires_atandrate_limit_per_minutenow because later capture/rate-limit features need persistent state. - (2026-05-11) F002 implemented in
server/migrations/20260511101000_create_inbound_webhook_deliveries_table.cjs. The table uses composite PK(tenant, delivery_id), indexes webhook/date, status/date, idempotency-window lookup, and replay links.inbound_webhook_idis nullable so rejected requests for unknown/disabled slugs can still be logged with limited detail without revealing config existence. - (2026-05-11) F003 implemented in
server/src/lib/inboundWebhooks/reservedIntegrationTypes.ts. Decision: reject reserved slugs rather than prefixing user slugs. The reserved set includes observed mapping values (ninjaone,tacticalrmm,tanium,xero,xero_csv,quickbooks_online,quickbooks_csv) plus PRD aliases (tactical_rmm,qbo,entra, Microsoft/Google aliases) to prevent future bundled integration collisions. - (2026-05-11) F004 implemented in
server/src/lib/inboundWebhooks/externalEntityMappings.ts.lookupAlgaEntityByExternalIdqueriestenant_external_entity_mappingswithtenant_id,integration_type = webhookSlug,alga_entity_type, andexternal_entity_id; no entity tables get external-ref columns. The helper accepts an optional Knex instance for future transactional action handlers. - (2026-05-11) F005 implemented in
server/src/lib/inboundWebhooks/externalEntityMappings.ts.writeEntityMappingupserts by(tenant_id, integration_type, alga_entity_type, alga_entity_id), checks first for external ID collisions in the same realm, setssync_status='synced', and accepts an optional Knex instance for transactional create actions. - (2026-05-11) F006 implemented in
server/migrations/20260511102000_add_inbound_webhook_permissions.cjsandserver/seeds/dev/47_permissions.cjs. Addedinbound_webhookactionscreate/read/update/delete/replayas MSP-only permissions and backfilled them onto existing MSP Admin roles. The migration intentionally does not grant Manager/Technician roles by default, matching T007. - (2026-05-11) F007 implemented in
packages/core/src/lib/featureFlagRuntime.tsandserver/src/lib/inboundWebhooks/featureFlag.ts.inbound_webhooks_enableddefaults tofalsewhen PostHog is unavailable; later UI and receiver-route gates should callisInboundWebhooksEnabled({ tenantId, userId }). - (2026-05-11) F010 implemented in
server/src/lib/inboundWebhooks/types.ts. Added discriminated auth config unions, handler config unions, idempotency source types, runtime config/delivery shapes, andWorkflowWebhookEnvelope. Runtime-facing types use camelCase; DB mapping functions will translate from snake_case rows. - (2026-05-11) F011 implemented in
server/src/lib/inboundWebhooks/schemas.ts. Input schemas are snake_case to match server/API payloads, use discriminated unions forauth_configandhandler_config, enforce matching top-levelauth_type/handler_type, validate URL-safe lowercase slugs, and reject reserved integration slugs through F003 helper. - (2026-05-11) F012 implemented in
server/src/lib/actions/inboundWebhookActions.ts.listInboundWebhooksis wrapped inwithAuth, checksinbound_webhook:read, queriesinbound_webhookswithwhere({ tenant }), orders by update time/name, maps snake_case DB rows to camelCase runtime types, and redacts any raw auth secret/token fields by returning only vault paths/config metadata. - (2026-05-11) F013 implemented in
server/src/lib/actions/inboundWebhookActions.ts.getInboundWebhook(id)uses the same auth/permission/mapping path as list and scopes lookup by bothtenantandinbound_webhook_id, so missing or cross-tenant IDs returnnull. - (2026-05-11) F014 implemented in
server/src/lib/actions/inboundWebhookActions.ts.upsertInboundWebhookvalidates input with F011 schemas, checksinbound_webhook:create/update, enforces(tenant, slug)uniqueness before DB write, writes HMAC/Bearer/path-token secrets to tenant secret storage, returns the generated/provided secret once, and stores only vault path metadata inauth_config. - (2026-05-11) F015 implemented in
server/src/lib/actions/inboundWebhookActions.ts.deleteInboundWebhookchecksinbound_webhook:delete, fetches by(tenant, inbound_webhook_id), deletes the config row, and deletes the associated HMAC/Bearer/path-token tenant secret. Delivery rows are retained with nullableinbound_webhook_idper the F002 migration rather than cascaded. - (2026-05-11) F016 implemented in
server/src/lib/actions/inboundWebhookActions.ts.rotateInboundWebhookSecretchecksinbound_webhook:update, rejects IP allowlist configs, writes a new generated secret/token to the existing or generated tenant-secret vault path, updatesauth_configmetadata if needed, and returns the new value once. - (2026-05-11) F017 implemented in
server/src/lib/actions/inboundWebhookActions.ts.setInboundWebhookActiveStatechecksinbound_webhook:update, updatesis_activeby(tenant, inbound_webhook_id), clearsauto_disabled_atwhen re-enabling, and returns the redacted config view. - (2026-05-11) F018 implemented in
server/src/lib/actions/inboundWebhookActions.ts.listInboundDeliveries(filter, page, limit)checksinbound_webhook:read, filters by tenant plus optional webhook/status/date range, paginates with a 100-row cap, and maps rows to camelCase delivery views. - (2026-05-11) F019 implemented in
server/src/lib/actions/inboundWebhookActions.ts.getInboundDelivery(id)checksinbound_webhook:read, scopes by(tenant, delivery_id), and returnsnullfor missing/cross-tenant deliveries. - (2026-05-11) F020 implemented in
server/src/lib/actions/inboundWebhookActions.ts.replayInboundDeliveryrequiresinbound_webhook:replay, loads the original delivery and current webhook config by tenant, creates a new linked delivery row (is_replay=true,replayed_from=<original_id>), and dispatches through the shared current-config dispatcher inserver/src/lib/inboundWebhooks/dispatcher.ts. - (2026-05-11) F021 implemented in
server/src/lib/actions/inboundWebhookActions.ts.captureSamplePayload(id)checksinbound_webhook:update, scopes by(tenant, inbound_webhook_id), and setssample_capture_expires_atto now + 5 minutes. - (2026-05-11) F022 implemented in
server/src/lib/actions/inboundWebhookActions.ts.clearSamplePayload(id)checksinbound_webhook:update, scopes by(tenant, inbound_webhook_id), clears bothsample_payloadandsample_capture_expires_at, and returns the redacted config. - (2026-05-11) F023 implemented in
server/src/lib/actions/inboundWebhookActions.ts.sendInboundWebhookTestrequires update permission, creates an auth-verified synthetic delivery with caller-provided body/headers, and dispatches in-process through the samedispatchInboundWebhookHandlerpath as receiver/replay. - (2026-05-11) F050 implemented in
server/src/lib/inboundWebhooks/actions/registry.ts. AddedregisterAction,getAction,listActions, duplicate-name rejection, deterministic list ordering by entity/name, and a test-only clear helper. - (2026-05-11) F051 implemented in
server/src/lib/inboundWebhooks/actions/registry.ts.InboundActionDefinitionincludes{ name, entityType, displayName, description, targetFields, handle(ctx, mappedValues) }and returns a structuredInboundActionResult. - (2026-05-11) F052 implemented in
server/src/lib/inboundWebhooks/actions/registry.ts.InboundActionTargetFieldincludes{ name, type, required, description, enumValues?, refEntityType? };refEntityTypeis an additive helper for PRDref-to-entityfields. - (2026-05-11) F053 implemented in
server/src/lib/inboundWebhooks/actions/bootstrap.tsandinitializeApp. Bootstrap currently imports@alga-psa/tickets/actions/inboundActions; add future package action modules here as they land.registerActionwas made generic so package actions can keep typed mapped-value payloads. - (2026-05-11) F054 implemented in
server/src/lib/inboundWebhooks/actions/mappingEvaluator.ts.evaluateFieldMappingevaluates each mapped target field using the existing workflow JSONata runtime (@alga-psa/workflows/runtime/expressionEngine) with bothbodyandpayloadbound to the inbound request body. - (2026-05-11) F055 implemented in
server/src/lib/inboundWebhooks/dispatcher.tsand the receiver/replay/test outcome paths. The dispatcher validates required fields and primitive target field types after JSONata evaluation; errors are caught by callers and persisted tohandler_outcome.errorwithdispatch_status='failed'. - (2026-05-11) F056 verified/implemented through the existing direct-action dispatcher plus action return contracts. Ticket/client/invoice
*ByExternalIdlookup misses returnsuccess:falsewithlookup_missmessages, anddispatchInboundWebhookHandlerconverts any unsuccessful action result into an exception so the receiver/replay/test callers persistdispatch_status='failed'rather than silently no-oping. - (2026-05-11) F060 implemented in
server/src/lib/inboundWebhooks/dispatcher.ts. Workflow handlers now call the existinglaunchPublishedWorkflowRunentrypoint with the normalized webhook envelope as the workflow payload,triggerType='event', event typeINBOUND_WEBHOOK_RECEIVED, and a delivery-scoped trigger/execution key. Envelope headers are filtered through the inbound header filter before being passed to workflow context. - (2026-05-11) F062 implemented via the existing
inbound_webhook_deliveries.handler_outcomeJSONB field rather than a dedicated column, matching the PRD data model. Successful workflow dispatch returns{ workflow_id, workflow_run_id, workflow_version, envelope }, and receiver/replay/test callers persist it on the delivery row when settingdispatch_status='dispatched'. - (2026-05-11) F063 implemented by the dispatcher/request-processor boundary.
launchPublishedWorkflowRunerrors propagate to the inbound request/replay/test callers and are persisted as failed deliveries withhandler_outcome.error; once launch returns aworkflow_run_id, later workflow execution failures are contained inworkflow_runsand do not mutate the already-dispatched inbound delivery. - (2026-05-11) F1010 implemented in
packages/tickets/src/actions/inboundActions.ts.createTicketregisters itself with the inbound action registry, uses sharedTicketModel.createTicketWithRetry, supports optional asset association, and writestenant_external_entity_mappingswhenexternal_idis mapped. - (2026-05-11) F1011 implemented in
packages/tickets/src/actions/inboundActions.ts.updateTicketByExternalIdresolves tickets throughtenant_external_entity_mappingsusingintegration_type=<webhookSlug>, updates status/priority/assignment/board and related ticket fields through sharedTicketModel.updateTicket, and returnssuccess:falsewithlookup_misswording when no mapping exists. - (2026-05-11) F1012 implemented in
packages/tickets/src/actions/inboundActions.ts.addTicketCommentByExternalIdresolves mapped tickets throughtenant_external_entity_mappings, appends comments through sharedTicketModel.createComment, supports internal/resolution/contact-author fields, and records inbound webhook metadata on the comment. - (2026-05-11) F1013 implemented in
packages/tickets/src/actions/inboundActions.ts.changeTicketStatusByExternalIdresolves mapped tickets by external ID and calls sharedTicketModel.updateTicket, so status/board compatibility validation is reused. - (2026-05-11) F1020 implemented in
packages/clients/src/actions/inboundActions.tsand bootstrapped from inbound action startup.upsertClientByExternalIdresolves existing clients viatenant_external_entity_mappings, updates tenant-scoped client rows when mapped, creates clients when absent, and writes a mapping row for newly created clients. - (2026-05-11) F1021 implemented in
packages/clients/src/actions/inboundActions.ts.setClientActiveByExternalIdresolves a mapped client and updatesis_inactivefrom the mappedactiveboolean; lookup misses returnsuccess:falsewithlookup_misswording for delivery failure recording. - (2026-05-11) F1030 implemented in
packages/clients/src/actions/inboundActions.ts.upsertContactByExternalIduses sharedContactModel.createContact/updateContact, writes contact mappings on create, and requires either directclient_idor resolvableclient_external_idwhen creating a new contact. - (2026-05-11) F1040 implemented in
packages/assets/src/actions/inboundActions.tsand bootstrapped from inbound action startup.upsertAssetByExternalIddetects mappedrmm_snapshotobjects, forcesprovider/integrationId/externalDeviceIdto the user webhook namespace, resolves optional client linkage, and delegates toingestNormalizedRmmDeviceSnapshot. - (2026-05-11) F1041 implemented in
packages/assets/src/actions/inboundActions.ts. When normm_snapshotis mapped,upsertAssetByExternalIdperforms a plain tenant-scoped upsert againstassets, resolves existing rows throughtenant_external_entity_mappings, and writes a mapping row on create. - (2026-05-11) F1050 implemented in
packages/billing/src/actions/inboundActions.tsand bootstrapped from inbound action startup.markInvoicePaidByExternalIdresolves invoices throughtenant_external_entity_mappings, setsstatus='paid', and records payment metadata incustom_fields. - (2026-05-11) F1051 implemented in
packages/billing/src/actions/inboundActions.ts.updateInvoiceStatusByExternalIdvalidates status through the action enum field and updates mapped invoices through the same tenant-scoped invoice helper. - (2026-05-11) F1052 implemented in
packages/billing/src/actions/inboundActions.ts. The shared invoice update helper returns the existing invoice without writing when the current status already equals the target status, making mark-paid idempotent. - (2026-05-11) F1060 implemented in
packages/scheduling/src/actions/inboundActions.tsand bootstrapped from inbound action startup.createTimeEntryaccepts mapped user/work item/service/start/duration/billable fields, validates tenant-scoped references, computeswork_datefrom the mapped user's timezone, creates or reuses the matching draft time sheet, inserts atime_entriesrow, publishes the standardTIME_ENTRY_CREATEDevent, and writes atenant_external_entity_mappingsrow whenexternal_idis mapped. - (2026-05-11) F1070 implemented in
packages/projects/src/actions/inboundActions.tsand bootstrapped from inbound action startup.createProjectTaskresolves its parent project from either directproject_idor a webhook-scopedproject_external_idmapping, defaults to the first project phase and first effective task status mapping when omitted, creates throughProjectTaskModel.addTaskto preserve WBS/order behavior, and writes aproject_taskmapping whenexternal_idis mapped. - (2026-05-11) F1071 implemented in
packages/projects/src/actions/inboundActions.ts.updateProjectTaskStatusByExternalIdresolves the project task viatenant_external_entity_mappings, validates the targetproject_status_mapping_idagainst the task's project, updates throughProjectTaskModel.updateTaskStatus, and returnssuccess:falsewithlookup_misswording when the external task ID is unmapped or stale. - (2026-05-11) F1080 implemented in
packages/tags/src/actions/inboundActions.tsand bootstrapped from inbound action startup.addTagToEntityByExternalIdresolves the target throughtenant_external_entity_mappings, gets/creates atag_definitionsrow, idempotently insertstag_mappings, and returnslookup_misswhen the external entity is unmapped. V1 is limited to the entity types currently supported by the tag model (client,contact,ticket,project,project_task); unsupported entity types fail with a clear validation error. - (2026-05-11) F061 implemented in
server/src/lib/inboundWebhooks/workflowEnvelope.ts.buildWorkflowWebhookEnvelopereturns the documented shape{source, body, headers, verified, delivery_id, idempotency_key, received_at}and normalizesreceived_atto ISO string. - (2026-05-11) F070 implemented in
shared/workflow/expression-authoring/adapters/webhookPayloadContextAdapter.tsand exported fromadapters/index.ts. The adapter exposes context roots/path options for captured webhook payloads. Also correctedevaluateFieldMappingto evaluate JSONata directly against the request body, matching PRD examples likealert.message. - (2026-05-11) F071 implemented in
shared/workflow/expression-authoring/adapters/webhookPayloadContextAdapter.ts. The adapter infers schema nodes recursively from captured samples, supports nested objects/arrays/primitives/nulls, and builds path options via existingbuildPathOptionsFromContextRoots. - (2026-05-11) F072 implemented in
server/src/components/settings/security/inbound/InboundWebhookMappingField.tsx. The new client component wraps the existing EE workflow-designerExpressionTextArea, feeds it autocomplete options from the webhook payload context adapter, and exposes a compact prop API for the upcoming inbound direct-action field rows. - (2026-05-11) F080 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. The Settings webhook panel is now a tabbed shell withInboundandOutboundtabs; the existing outbound implementation was moved intact intoOutboundWebhooksSetup, and the inbound tab has a translated placeholder until the list view lands in F081. Also widened the sharedTabsTriggerprop type so interactive tab IDs compile. - (2026-05-11) F081 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx.InboundWebhooksListViewloads tenant-scoped inbound configs vialistInboundWebhooks, fetches the latest delivery per webhook for the last-delivery column, derives receiver URLs from the existing tenant portal slug helper, and renders a DataTable with name/URL, handler, last delivery, active state, and row action menu. - (2026-05-11) F082 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. Added an inbound create/edit dialog shell with an Identity section for name, URL-safe slug, and description; the New button and row Edit action open the dialog, with all new labels localized and interactive controls carrying stable IDs. Save remains disabled until the auth/handler sections land. - (2026-05-11) F083 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. The inbound dialog now includes an Auth section with a method dropdown and conditional fields for HMAC signature header, bearer token, IP/CIDR allowlist, and path-token query parameter/token. Existing configs hydrate non-secret auth metadata, while secret fields stay blank for later one-time-display/rotate flows. - (2026-05-11) F084 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. Added the inbound one-time secret modal with copy/download/close controls and localized warning copy. The modal is state-driven and ready for the eventual create/rotate save paths to reveal generated HMAC/Bearer/path-token secrets exactly once. - (2026-05-11) F085 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. The inbound dialog now has an Idempotency section with a source dropdown (headerorjsonata), conditional label/placeholder for the key source value, and a duplicate-window seconds input defaulting to 24 hours. - (2026-05-11) F086 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. The inbound dialog now has a Handler section with a direct-action/workflow selector and conditional panels. Direct-action registry selection, mapping rows, and workflow picker remain separate follow-on UI items. - (2026-05-11) F087 implemented with
listInboundWebhookActionsinserver/src/lib/actions/inboundWebhookActions.tsand the inbound dialog action selector. The server action bootstraps the registry, checksinbound_webhook:read, returns serializable action metadata/target fields withouthandle(), and the UI populates the direct-action dropdown from that live registry response. - (2026-05-11) F088 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. Selecting a direct action now renders every declared target field as a row with required/type metadata and anInboundWebhookMappingField, which wraps the reused workflow-designerExpressionTextAreaand sample-payload autocomplete. - (2026-05-11) F089 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. Added a sample payload capture panel with saved-webhook gating, capture-window/sample status text, and a button that callscaptureSamplePayloadthen refreshes local config state. New webhooks show create-first guidance because the receiver URL does not exist until save. - (2026-05-11) F090 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsxplus a small focus callback extension to the reusedExpressionTextArea. Direct-action mappings now show a side panel of captured sample paths generated bybuildWebhookPayloadExpressionPathOptions; clicking a path inserts it into the focused target-field mapping. - (2026-05-11) F091 implemented with
listInboundWorkflowOptionsinserver/src/lib/actions/inboundWebhookActions.tsand the workflow handler branch inAdminWebhooksSetup.tsx. The UI now lists visible tenant workflow definitions in a dropdown and shows the documented normalized envelope shape the workflow receives. - (2026-05-11) F092 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. The inbound dialog now includes an active-state switch; saved webhooks persist throughsetInboundWebhookActiveState, unsaved forms keep local state, and an auto-disable banner renders whenautoDisabledAtis set. - (2026-05-11) F093 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. Saved inbound webhook dialogs now loadlistInboundDeliveries({ inboundWebhookId })and render a paginated delivery table with received time, dispatch status, response status, and duration. - (2026-05-11) F094 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. Delivery rows now expose a View action that opens a right-side drawer with request headers/body, response body/status, duration, dispatch status, and handler outcome/error details. - (2026-05-11) F095 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. The delivery detail drawer now includes a Replay action that callsreplayInboundDelivery, updates the drawer to the replayed delivery, and refreshes the paginated delivery log for the current webhook. - (2026-05-11) F096 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. Saved webhook dialogs now include a Send Test flow with JSON body and header-line editors; it parses the inputs, callssendInboundWebhookTest, refreshes deliveries, and opens the resulting delivery detail. - (2026-05-11) F097 implemented/verified by scanning the inbound UI additions in
AdminWebhooksSetup.tsxandserver/src/components/settings/security/inbound/. User-facing labels, button text, helper copy, status text, and placeholders are routed throught('security.webhooks.inbound...')locale keys. Server-provided action/workflow names and JSON contract property names remain data/technical identifiers rather than hardcoded UI copy. - (2026-05-11) F098 implemented/verified by scanning the inbound tab/dialog/drawer UI controls. New interactive controls (tabs, buttons, selects, inputs, switches, text areas, tables, row menu items, path buttons, dialogs, and drawer) all have stable kebab-case
idattributes. - (2026-05-11) F100 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsx. The Settings Webhooks shell now usesuseFeatureFlag('inbound_webhooks_enabled', { defaultValue:false }), defaults to the existing Outbound tab, and only renders the Inbound tab/content when the flag is enabled. - (2026-05-11) F101 implemented in
server/src/lib/inboundWebhooks/requestProcessor.ts. The receiver route resolves the tenant slug first, evaluatesinbound_webhooks_enabledwith the actual tenant ID, and returns a bare404before config lookup/dispatch when disabled. - (2026-05-11) F102 implemented/verified in
server/src/lib/actions/inboundWebhookActions.ts. Every exported inbound webhook server action is wrapped inwithAuthand callsassertInboundWebhookPermissionwith the appropriateread/create/update/delete/replayaction before querying or mutating tenant data. - (2026-05-11) F200 implemented in
server/src/app/api/v1/inbound-webhooks/route.ts.GET /api/v1/inbound-webhookscalls the tenant-scopedlistInboundWebhooksserver action and returns{ data }, with errors flowing through the existing API error middleware. - (2026-05-11) F201 implemented in
server/src/app/api/v1/inbound-webhooks/route.ts.POST /api/v1/inbound-webhooksparses JSON input, delegates creation toupsertInboundWebhook, returns201with the redacted config plus one-time secret, and reuses the server action'swithAuth/permission/secret-vault behavior. - (2026-05-11) F202 implemented in
server/src/app/api/v1/inbound-webhooks/[id]/route.ts.GET,PUT, andDELETEdelegate to the existing server actions, return 404 on missing configs for GET, force the path id into update payloads, and return204on delete while preserving centralized auth and tenant scoping. - (2026-05-11) F203 implemented in
server/src/app/api/v1/inbound-webhooks/[id]/rotate-secret/route.ts. The endpoint callsrotateInboundWebhookSecretand returns the redacted config plus one-time replacement secret, preserving the server action's IP-allowlist rejection and vault update behavior. - (2026-05-11) F204 implemented in
server/src/app/api/v1/inbound-webhooks/[id]/test/route.ts.POST /testaccepts the synthetic body/headers payload, delegates tosendInboundWebhookTest, and returns the created delivery with202after in-process dispatch records its outcome. - (2026-05-11) F205 implemented in
server/src/app/api/v1/inbound-webhooks/[id]/capture-sample/route.ts.POSTenables a five-minute capture window viacaptureSamplePayload;DELETEclears both saved sample and capture state viaclearSamplePayload. - (2026-05-11) F206 implemented in
server/src/app/api/v1/inbound-webhooks/[id]/deliveries/route.ts. The route lists deliveries throughlistInboundDeliveries, forces the path webhook id into the filter, supportspage,limit,status,date_from/dateFrom, anddate_to/dateTo, and returns pagination metadata. - (2026-05-11) F207 implemented in
server/src/app/api/v1/inbound-webhooks/[id]/deliveries/[deliveryId]/route.ts. The detail endpoint delegates togetInboundDeliveryand returns 404 unless the tenant-scoped delivery belongs to the webhook id in the URL. - (2026-05-11) F208 implemented in
server/src/app/api/v1/inbound-webhooks/[id]/deliveries/[deliveryId]/replay/route.ts. Replay first verifies the tenant-scoped delivery belongs to the URL webhook, then callsreplayInboundDeliveryand returns the new linked delivery with202. - (2026-05-11) F209 implemented in
server/src/app/api/v1/inbound-webhooks/actions/route.ts.GET /actionsreturnslistInboundWebhookActions()so SDK clients get the same registered action definitions and target field schemas as the Settings UI. - (2026-05-11) F210 implemented in
server/src/lib/api/openapi/routes/inboundWebhooks.tsand registered fromserver/src/lib/api/openapi/index.ts. The file registers all/api/v1/inbound-webhooks/*management and action-discovery paths with auth/RBAC metadata; named component schemas remain split into the later F212-F218 items. - (2026-05-11) F211 implemented in
server/src/lib/api/openapi/routes/inboundWebhooks.ts. The templated receiver endpoint/api/inbound/{tenantSlug}/{webhookSlug}is documented with tenant/webhook params, configurable signature/idempotency headers, generic JSON body, feature-flag metadata, and 200/401/404/429/5xx response surface. - (2026-05-11) F212 implemented in
server/src/lib/api/openapi/routes/inboundWebhooks.ts. Registered named componentInboundWebhookConfigusing the camelCase runtime/server-action response shape with redacted auth metadata, handler/idempotency fields, sample capture state, rate limit, active state, and timestamps. - (2026-05-11) F213 implemented in
server/src/lib/api/openapi/routes/inboundWebhooks.ts. Registered namedInboundWebhookCreateInputandInboundWebhookUpdateInputcomponents using the snake_case API/server-action payload shape; POST uses create and PUT uses update. - (2026-05-11) F214 implemented in
server/src/lib/api/openapi/routes/inboundWebhooks.ts. Registered namedInboundWebhookAuthConfigas anauth_typediscriminated union covering HMAC, Bearer, IP allowlist, and path-token configs, including one-time create fields and stored vault-path metadata. - (2026-05-11) F215 implemented in
server/src/lib/api/openapi/routes/inboundWebhooks.ts. Registered namedInboundWebhookHandlerConfigas ahandler_typediscriminated union for direct actions (actionplus JSONatafield_mapping) and workflow triggers (workflow_id). - (2026-05-11) F216 implemented in
server/src/lib/api/openapi/routes/inboundWebhooks.ts. Registered namedInboundWebhookDeliverywith persisted request/response fields, auth/dispatch statuses, handler outcome, replay linkage, duration, and timestamps. - (2026-05-11) F217 implemented in
server/src/lib/api/openapi/routes/inboundWebhooks.ts. Registered namedInboundActionTargetFieldandInboundActionDefinitioncomponents and wired the action discovery response toInboundActionDefinition[]. - (2026-05-11) F218 implemented in
server/src/lib/api/openapi/routes/inboundWebhooks.ts. Registered namedWorkflowWebhookEnvelopedocumenting the workflowcontext.inputpayload: source slug, parsed body, filtered headers, verified flag, delivery id, idempotency key, and received timestamp. - (2026-05-11) F220 implemented by running the OpenAPI generator from the
sdkworkspace for both CE and EE editions. This regeneratedalga-openapi.{ce,ee}.{json,yaml}plus the CE aliasalga-openapi.{json,yaml}; the CE YAML now contains the/api/v1/inbound-webhooks/*management paths,/api/inbound/{tenantSlug}/{webhookSlug}, and inbound webhook components. - (2026-05-11) F221 implemented in
server/src/test/unit/api/inboundWebhooksOpenApi.contract.test.ts. The contract test builds the CE OpenAPI document and asserts every management route has the expected method, API-key security, tenant-scoped inbound RBAC metadata, path params, request schema refs, and response schema refs. - (2026-05-11) F222 implemented in
server/src/test/unit/api/inboundWebhooksOpenApi.contract.test.ts. Added action discovery assertions for/api/v1/inbound-webhooks/actions, including response data asInboundActionDefinition[]and component shape coverage forInboundActionDefinitionplusInboundActionTargetField. - (2026-05-11) F223 implemented/verified across
server/src/app/api/v1/inbound-webhooks/**/route.ts. Every REST handler imports and delegates toserver/src/lib/actions/inboundWebhookActions.ts; none importswithAuth,hasPermission, or tenant DB helpers directly, so the API surface shares the existing server-action auth/permission path. - (2026-05-11) T001 implemented in
server/src/test/unit/migrations/inboundWebhookMigrations.test.ts. Static migration contract verifiesinbound_webhookshastenant,inbound_webhook_id, composite PK(tenant, inbound_webhook_id), tenant-scoped uniqueness/indexes, and Citus distribution ontenant. - (2026-05-11) T002 implemented in
server/src/test/unit/migrations/inboundWebhookMigrations.test.ts. Static migration contract verifiesinbound_webhook_deliverieshas composite PK(tenant, delivery_id), tenant-scoped webhook and replay foreign keys, idempotency index, and Citus distribution ontenant. - (2026-05-11) T003 implemented in
server/src/test/unit/migrations/inboundWebhookMigrations.test.ts. Static migration contract verifies slug uniqueness is(tenant, slug)viainbound_webhooks_tenant_slug_uniqueand rejects global slug-only unique constraints. - (2026-05-11) T004 implemented in
server/src/test/unit/inboundWebhooks/externalEntityMappings.test.ts. Unit test callslookupAlgaEntityByExternalIdwith a fake Knex builder and verifies it queriestenant_external_entity_mappingswithtenant_id,integration_type=<webhookSlug>,alga_entity_type, andexternal_entity_idfilters. - (2026-05-11) T004a implemented in
server/src/test/unit/inboundWebhooks/externalEntityMappings.test.ts. Unit test verifieswriteEntityMappingtreats an existing mapping to the same Alga entity as idempotent, checks the same external-id collision lookup, and upserts on(tenant_id, integration_type, alga_entity_type, alga_entity_id). - (2026-05-11) T004b implemented in
server/src/test/unit/inboundWebhooks/schemas.test.ts. Schema test verifiesinboundWebhookUpsertInputSchemarejects reserved bundled integration slugs such asninjaone, preventing user webhooks from polluting bundled integration mapping namespaces. - (2026-05-11) T005 implemented in
server/src/test/unit/migrations/inboundWebhookMigrations.test.ts. Static migration guard verifies inbound webhook migrations use table-existence checks, Citus distribution guards,CREATE INDEX IF NOT EXISTSfor delivery indexes, and safedropTableIfExistsdown migrations so re-running is safe. - (2026-05-11) T006 implemented in
server/src/test/unit/migrations/inboundWebhookPermissions.test.ts. Source-level permission contract verifies the inbound webhook action set (create/read/update/delete/replay) is present in the migration and dev seed as MSP-only permissions, and that the migration grants them to the MSP Admin role throughrole_permissions. - (2026-05-11) T007 implemented in
server/src/test/unit/migrations/inboundWebhookPermissions.test.ts. Source-level permission contract verifies the inbound webhook migration grants only through the MSP Admin role lookup and does not add default role-permission inserts for Manager, Technician, Client, or client-portal roles. - (2026-05-11) T010 implemented in
server/src/test/unit/inboundWebhooks/schemas.test.ts. Zod schema test verifiesinboundWebhookUpsertInputSchemarejects unsupportedauth_typevalues before configs can be persisted. - (2026-05-11) T011 implemented in
server/src/test/unit/inboundWebhooks/schemas.test.ts. Zod schema test verifies HMAC auth configs must include a non-emptysignature_header, preventing un-verifiable HMAC webhook configs. - (2026-05-11) T012 implemented in
server/src/test/unit/inboundWebhooks/schemas.test.ts. Zod schema test verifies direct-action handler configs must include a non-emptyactionname before field mappings are accepted. - (2026-05-11) T013 implemented in
server/src/test/unit/inboundWebhooks/schemas.test.ts. Zod schema test verifies workflow handler configs must include aworkflow_idwhenhandler_type='workflow'. - (2026-05-11) T014 implemented in
server/src/test/unit/inboundWebhooks/schemas.test.ts. Zod schema test verifies webhook slugs reject uppercase, spaces, punctuation, and other URL-unsafe characters via the URL-safe lowercase slug rule. - (2026-05-11) T020 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. MockedwithAuth, RBAC, and Knex verifylistInboundWebhookscreates a tenant-scoped Knex instance, checksinbound_webhook:read, applieswhere({ tenant }), and maps only rows for the current tenant. - (2026-05-11) T021 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Mocked upsert path verifiesupsertInboundWebhookchecks(tenant, slug)for collisions, rejects duplicate slugs in the same tenant, and stops before writing secrets or inserting rows. - (2026-05-11) T022 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Mocked create path verifiesupsertInboundWebhookscopes slug collision checks by current tenant, allowing a webhook slug already used by another tenant to be inserted fortenant-a. - (2026-05-11) T023 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Mocked create path verifies HMAC secrets are written via tenant secret storage, inserted DBauth_configstores onlysecret_vault_pathmetadata, and the returned secret is one-time. - (2026-05-11) T024 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. MockedgetInboundWebhooklookup verifies raw secret fields present in DBauth_configare redacted from the returned config view. - (2026-05-11) T025 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Mocked rotation verifiesrotateInboundWebhookSecretchecksinbound_webhook:update, overwrites the existing tenant-secret vault path with the newly returned one-time secret, updates only vault metadata inauth_config, and does not leak the new secret through the webhook config response. - (2026-05-11) T026 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Decision confirmed: delivery rows are retained after webhook deletion. The migration FK usesON DELETE SET NULL, and the server-action test verifiesdeleteInboundWebhookdeletes only the tenant-scoped config row, deletes the stored auth secret, and never deletesinbound_webhook_deliveries. - (2026-05-11) T027 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Permission-denial test setshasPermission=falseand verifieslistInboundWebhooksthrowsForbidden: inbound_webhook:read permission requiredbefore querying webhook tables. - (2026-05-11) T028 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Runtime test verifiesgetInboundWebhook,upsertInboundWebhook, anddeleteInboundWebhookall consume the tenant supplied bywithAuth, callcreateTenantKnex('tenant-a'), and scope their lookup/insert/delete payloads by that tenant. - (2026-05-11) T030 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Receiver test keeps the real HMAC verifier, mocks tenant/db/rate-limit/delivery/dispatch seams, sends a correctly signedPOST /api/inbound/<tenant>/<slug>request, and verifies200with{delivery_id}, verified delivery creation, dispatch, and delivery outcome persistence. - (2026-05-11) T031 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Invalid HMAC receiver test verifies a bodyless401, a single rejected-auth delivery withauth_status='rejected_signature'and no request body, and no dispatch/outcome update. - (2026-05-11) T032 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Mismatched HMAC header-name test signs the body correctly but sends it inX-Wrong-Signature; because the config expectsX-Signature, the receiver returns a bodyless401, logsrejected_signature, and skips dispatch. - (2026-05-11) T033 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Bearer-auth receiver test swaps in a bearer webhook config, reads the mocked tenant-secret token through the real verifier, and verifies a validAuthorization: Bearer ...request creates a verified delivery, dispatches, and returns200 {delivery_id}. - (2026-05-11) T034 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Bad Bearer token test verifies a bodyless401, rejected-auth delivery metadata withauth_status='rejected_bearer', no persisted request body, and no dispatch. - (2026-05-11) T035 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. IP allowlist success test uses the real CIDR matcher withX-Forwarded-For, verifies the first forwarded IP is accepted, no tenant secret is read, and a verified delivery dispatches. - (2026-05-11) T036 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. IP allowlist rejection test verifies a request from outside the configured CIDR returns a bodyless401, logsauth_status='rejected_ip'with no request body, and skips dispatch. - (2026-05-11) T037 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Path-token success test uses the real verifier with the configuredtokenquery parameter, verifies tenant-secret lookup by vault path, and confirms a valid token creates a verified delivery and dispatches. - (2026-05-11) T038 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Invalid path-token test verifies a mismatched?token=returns a bodyless401, logsauth_status='rejected_no_auth'with no request body, and skips dispatch. - (2026-05-11) T039 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Unknown tenant-slug test verifies the receiver returns the same bodyless401shape used for auth failures and stops before feature-flag lookup, tenant context, config lookup, delivery persistence, or dispatch. - (2026-05-11) T040 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Unknown webhook-slug test under a valid tenant verifies the receiver returns the same bodyless401, persists a limited rejected-auth delivery withinbound_webhook_id=null, omits request body, and skips dispatch. - (2026-05-11) T041 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Disabled webhook test verifiesis_active=falseshort-circuits before secret lookup/auth verification, returns the same bodyless401, logs limited rejected-auth metadata against the webhook id, and skips dispatch. - (2026-05-11) T042 implemented in
server/src/test/unit/inboundWebhooks/authVerifier.test.ts. HMAC verifier test mutates one byte of an otherwise valid same-length signature and spies oncrypto.timingSafeEqual, proving the HMAC path still performs timing-safe comparison with equal-length buffers instead of early-returning on byte mismatch. - (2026-05-11) T043 implemented in
server/src/test/unit/inboundWebhooks/authVerifier.test.ts. Bearer verifier test supplies a same-length wrong token, spies oncrypto.timingSafeEqual, and verifies the bearer path rejects withrejected_beareronly after timing-safe comparison with equal-length buffers. - (2026-05-11) T044 implemented in
server/src/test/unit/inboundWebhooks/headerFilter.test.ts. Header-filter test verifies persisted inbound headers stripAuthorization,Cookie,Set-Cookie,Proxy-Authorization, andX-Api-Keywhile preserving safe lowercase headers likecontent-typeandx-request-id. - (2026-05-11) T050 implemented in
server/src/test/unit/inboundWebhooks/idempotency.test.ts. Header-source idempotency test verifies configured header names are extracted case-insensitively from bothHeadersand plain record inputs, trim whitespace, and use the first value when a header array is supplied. - (2026-05-11) T051 implemented in
server/src/test/unit/inboundWebhooks/idempotency.test.ts. JSONata-source idempotency test evaluatesalert.idagainst the parsed request body using the real workflow expression runtime and trims the resulting key. - (2026-05-11) T052 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Duplicate idempotency receiver test verifies a duplicate key within the configured 24h window returns200with the originaldelivery_id, creates a new delivery row markeddispatch_status='duplicate', recordshandler_outcome.duplicate_of, and does not dispatch. - (2026-05-11) T053 implemented in
server/src/test/unit/inboundWebhooks/idempotency.test.ts. Duplicate lookup test freezes time, verifies the query appliesreceived_at >= now - windowSecondsplus tenant/webhook/key/status filters, and returnsnullwhen no in-window row exists so the receiver can fresh-dispatch the same key after expiry. - (2026-05-11) T054 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Decision locked: if an idempotency source is configured but yields no key, the receiver accepts and dispatches once withidempotency_key=nullrather than rejecting the request; dedupe lookup receivesnulland returns no duplicate. - (2026-05-11) T055 implemented in
server/src/test/unit/inboundWebhooks/idempotency.test.ts. Duplicate lookup is explicitly scoped byinbound_webhook_idalong with tenant and idempotency key, so two webhook configs can receive the same external idempotency key without deduping each other. - (2026-05-11) T060 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Verified requests now have a test assertingcreateInboundDeliveryreceivesauthStatus='verified'plus the parsed body and is invoked beforedispatchInboundWebhookHandler, preserving visible failed-dispatch rows. - (2026-05-11) T061 implemented in
server/src/test/unit/inboundWebhooks/deliveryPersistence.test.ts. Rejected-auth persistence now asserts a row is still inserted with tenant/webhook metadata and response status, whilerequest_bodyis forced tonulland sensitive headers are filtered. - (2026-05-11) T062 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Dispatch exceptions now have regression coverage: the receiver returns500 {delivery_id, error:'dispatch_failed'}and updates the existing verified delivery todispatch_status='failed'with the exception message inhandler_outcome.error. - (2026-05-11) T070 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Over-limit requests now assertcheckInboundWebhookRateLimit(tenant, webhookId)is enforced after auth/JSON parse, return429withretry-after, persist a failed verified delivery, and skip dispatch. - (2026-05-11) T071 implemented in
server/src/test/unit/inboundWebhooks/rateLimitConfig.test.ts. Rate-limit isolation by webhook is covered by asserting the shared token bucket is called with namespacewebhook-in, the tenant ID, and each webhook ID as a distinct bucket dimension. - (2026-05-11) T072 implemented in
server/src/test/unit/inboundWebhooks/rateLimitConfig.test.ts. Rate-limit isolation by tenant is covered by asserting the same webhook ID under two tenants calls the shared token bucket with different tenant dimensions. - (2026-05-11) T080 implemented in
server/src/test/unit/inboundWebhooks/sampleCapture.test.ts. Sample capture now has direct coverage for the atomic conditional update that stores the verified body only when capture is active andsample_payloadis still null, then clearssample_capture_expires_at. - (2026-05-11) T081 implemented in
server/src/test/unit/inboundWebhooks/sampleCapture.test.ts. Expired/otherwise non-matching capture windows now returnfalsewhen the conditional update affects zero rows, which prevents late requests from overwriting an existing captured sample. - (2026-05-11) T082 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts.clearSamplePayloadnow has server-action coverage forinbound_webhook:updatepermission, tenant-scoped update by webhook id, and clearing bothsample_payloadandsample_capture_expires_at. - (2026-05-11) T090 implemented in
server/src/test/unit/inboundWebhooks/actionRegistry.test.ts. Registry ordering is now locked:registerActionstores definitions andlistActions()returns a deterministic flat list grouped byentityTypeordering and then action name. - (2026-05-11) T091 implemented in
server/src/test/unit/inboundWebhooks/actionRegistry.test.ts. Duplicate action registration now has explicit coverage and must throwInbound action "<name>" is already registered, matching the registry's fail-fast behavior. - (2026-05-11) T092 implemented in
server/src/test/unit/inboundWebhooks/actionRegistry.test.ts. Added a source-level bootstrap contract that requires all seven v1 package contribution imports (tickets, clients/contacts, assets, billing, scheduling, projects, tags), plus bootstrap calls from app initialization and action discovery beforelistActions(). - (2026-05-11) T093 implemented in
server/src/test/unit/inboundWebhooks/mappingEvaluator.test.ts. JSONata field mapping now has direct coverage against the raw request body, preserving PRD-style expressions likealert.messageinstead of requiring a wrapper object. - (2026-05-11) T094 implemented in
server/src/lib/inboundWebhooks/dispatcher.ts,requestProcessor.ts, andserver/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Mapping/evaluation failures are now classified asInboundWebhookMappingErrorand recorded as failed deliveries withresponse_status=400/error='mapping_failed'instead of surfacing as internal 500 dispatch failures. - (2026-05-11) T095 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Missing required mapped fields now have explicit delivery coverage: the request processor records a failed delivery with the dispatcher-provided field/action error and returns400witherror='mapping_failed'. - (2026-05-11) T096 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. External-ID lookup misses now have receiver-level delivery coverage: action lookup-miss errors are persisted tohandler_outcome.errorwithdispatch_status='failed', preserving thelookup_missreason for debugging. - (2026-05-11) T1010 implemented in
server/src/test/unit/inboundWebhooks/ticketInboundActions.test.ts. The test imports the ticket package's inbound action contribution, retrievescreateTicketfrom the registry, and verifies mapped fields plus webhook metadata are passed toTicketModel.createTicketWithRetrywith the current tenant and transaction. - (2026-05-11) T1011 implemented in
server/src/test/unit/inboundWebhooks/ticketInboundActions.test.ts. ThecreateTicketaction now has coverage for optional mappedexternal_id, asserting it writes aticketmapping throughwriteEntityMappingwithintegration_type=<webhookSlug>and delivery metadata inside the transaction. - (2026-05-11) T1012 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Successful direct-action delivery updates now assert thecreateTicketdispatch outcome is persisted tohandler_outcomewithentity_type='ticket',entity_id=<ticket_id>, and ticket number metadata. - (2026-05-11) T1013 implemented in
server/src/test/unit/inboundWebhooks/ticketInboundActions.test.ts.updateTicketByExternalIdnow has coverage for resolving the ticket throughtenant_external_entity_mappingsvialookupAlgaEntityByExternalIdand applying mapped status, priority, and assignee fields throughTicketModel.updateTicketwith the current tenant. - (2026-05-11) T1014 implemented in
server/src/test/unit/inboundWebhooks/ticketInboundActions.test.ts.updateTicketByExternalIdnow returns a structuredsuccess:falselookup_missresult and skipsTicketModel.updateTicketwhen the webhook-scoped external ID mapping is absent; T096 covers conversion of this failure class into a failed delivery row. - (2026-05-11) T1015 implemented in
server/src/test/unit/inboundWebhooks/ticketInboundActions.test.ts.addTicketCommentByExternalIdnow has coverage for resolving the mapped ticket, building the comment payload with internal/webhook metadata, and callingTicketModel.createCommentwith tenant and transaction scope. - (2026-05-11) T1016 implemented in
server/src/test/unit/inboundWebhooks/ticketInboundActions.test.ts.changeTicketStatusByExternalIdnow has coverage for callingTicketModel.updateTicketwith mapped status/board fields and propagating model validation errors for invalid statuses; request-processor failure persistence is covered by T062/T096. - (2026-05-11) T1020 implemented in
server/src/test/unit/inboundWebhooks/clientInboundActions.test.ts.upsertClientByExternalIdnow has coverage for the no-existing-mapping create path: tenant-scopedclientsinsert, default active/company payload normalization, andwriteEntityMappingwith webhook slug plus delivery metadata. - (2026-05-11) T1021 implemented in
server/src/test/unit/inboundWebhooks/clientInboundActions.test.ts.upsertClientByExternalIdnow has coverage for the mapped update path: lookup by webhook-scoped external ID, tenant-scopedclientsupdate, property merging, and no duplicate mapping write. - (2026-05-11) T1022 implemented in
server/src/test/unit/inboundWebhooks/clientInboundActions.test.ts.setClientActiveByExternalIdnow has coverage for resolving the mapped client, updatingclients.is_inactivefrom the mappedactiveflag, and returning active-state metadata. - (2026-05-11) T1030 implemented in
server/src/test/unit/inboundWebhooks/clientInboundActions.test.ts.upsertContactByExternalIdnow has coverage for creating a contact with direct client linkage, writing the webhook-scoped contact mapping, and updating an existing mapped contact throughContactModel.updateContact. - (2026-05-11) T1031 implemented in
server/src/test/unit/inboundWebhooks/clientInboundActions.test.ts.upsertContactByExternalIdnow rejects contact creation when neitherclient_idnor a resolvable webhook-scopedclient_external_idis available, and the test verifies no contact or mapping write occurs. - (2026-05-11) T1040 implemented in
server/src/test/unit/inboundWebhooks/assetInboundActions.test.ts.upsertAssetByExternalIdnow has coverage for RMM-shaped payloads delegating toingestNormalizedRmmDeviceSnapshot, resolving client linkage via webhook-scoped external ID, and stamping provider/integration/device identifiers to the user webhook namespace. - (2026-05-11) T1041 implemented in
server/src/test/unit/inboundWebhooks/assetInboundActions.test.ts. The RMM asset path now has regression coverage that a normalized device snapshot shaped like bundled RMM ingestion input preserves lifecycle, asset, extension, and metadata fields while only provider/integration/device namespace values are rewritten for the user webhook. - (2026-05-11) T1042 implemented in
server/src/test/unit/inboundWebhooks/assetInboundActions.test.ts. The non-RMM asset path now has coverage for tenant-scoped plainassetsinsert payloads, skipping shared RMM ingestion, and writing a webhook-scoped asset mapping row for later external-ID updates. - (2026-05-11) T1050 implemented in
server/src/test/unit/inboundWebhooks/invoiceInboundActions.test.ts.markInvoicePaidByExternalIdnow has coverage for resolving the invoice via webhook-scoped external mapping, updating status topaid, merging payment metadata intocustom_fields, and returning paid status metadata. - (2026-05-11) T1051 implemented in
server/src/test/unit/inboundWebhooks/invoiceInboundActions.test.ts.markInvoicePaidByExternalIdnow has lookup-miss coverage: absent webhook-scoped invoice mapping returnssuccess:falsewith alookup_missmessage and skips invoice reads/updates. - (2026-05-11) T1052 implemented in
server/src/test/unit/inboundWebhooks/invoiceInboundActions.test.ts.markInvoicePaidByExternalIdnow has idempotency coverage for invoices already inpaidstatus: the action returns success from the current row and skips duplicate status/custom-field updates. - (2026-05-11) T1053 implemented in
server/src/test/unit/inboundWebhooks/invoiceInboundActions.test.ts.updateInvoiceStatusByExternalIdnow has coverage for resolving a mapped invoice, applying a mapped non-paid status, and recording inbound delivery metadata incustom_fields. - (2026-05-11) T1060 implemented in
server/src/test/unit/inboundWebhooks/timeEntryInboundActions.test.ts.createTimeEntrynow has coverage for tenant-scoped reference checks, work-date/time-sheet resolution, creating a billabletime_entriesrow, writing optional external mappings, and publishingTIME_ENTRY_CREATED. - (2026-05-11) T1070 implemented in
server/src/test/unit/inboundWebhooks/projectTaskInboundActions.test.ts.createProjectTasknow has coverage for resolving a parent project throughtenant_external_entity_mappings, validating project/phase/status mapping scope, creating throughProjectTaskModel.addTask, and writing aproject_taskexternal mapping. - (2026-05-11) T1071 implemented in
server/src/test/unit/inboundWebhooks/projectTaskInboundActions.test.ts.updateProjectTaskStatusByExternalIdnow covers webhook-scoped task lookup throughtenant_external_entity_mappings, tenant-aware task/project resolution, target status validation, and the sharedProjectTaskModel.updateTaskStatuscall. - (2026-05-11) T1080 implemented in
server/src/test/unit/inboundWebhooks/tagInboundActions.test.ts.addTagToEntityByExternalIdnow covers resolving a mapped ticket, verifying the ticket still exists, getting/creating the tag definition, and inserting a tenant-scopedtag_mappingsrow. - (2026-05-11) T1081 implemented in
server/src/test/unit/inboundWebhooks/tagInboundActions.test.ts. The tag action now also coversentity_type='client', verifying the client table/id-column mapping and default tag color handling before insertingtag_mappings. - (2026-05-11) T1082 implemented in
server/src/test/unit/inboundWebhooks/tagInboundActions.test.ts. Unsupported tag entity types now have explicit validation coverage, including an assertion that the action rejects before creating a tenant Knex instance or attempting external-ID lookup. - (2026-05-11) T110 implemented in
server/src/test/unit/inboundWebhooks/workflowDispatcher.test.ts. Workflow handler dispatch now has coverage for launchinglaunchPublishedWorkflowRunwith the normalized inbound envelope, event trigger metadata, delivery-scoped execution keys, and the workflow run outcome returned to delivery persistence. - (2026-05-11) T111 implemented in
server/src/test/unit/inboundWebhooks/workflowEnvelope.test.ts. The workflow envelope builder now has deterministic coverage for the exact documented fields: source, body, headers, verified, delivery_id, idempotency_key, and ISO received_at. - (2026-05-11) T112 implemented in
server/src/test/unit/inboundWebhooks/workflowDispatcher.test.ts. Workflow dispatch now verifies sensitive inbound headers (Authorization,Cookie,Set-Cookie,X-Api-Key) are filtered before the envelope is passed tolaunchPublishedWorkflowRun. - (2026-05-11) T113 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. The receiver path now verifies a successful workflow dispatch outcome, includingworkflow_run_id, is persisted intohandlerOutcomewhen the delivery is markeddispatched. - (2026-05-11) T114 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Workflow trigger errors now have receiver-path coverage showing the delivery is updated todispatchStatus='failed'with the engine error message inhandlerOutcome. - (2026-05-11) T115 implemented in
server/src/test/unit/inboundWebhooks/workflowDispatcher.test.ts. Workflow dispatch now has launch-boundary coverage showing oncelaunchPublishedWorkflowRunreturns a run id/version, dispatch resolves with the workflow outcome and does not wait for or observe later workflow completion/failure. - (2026-05-11) T120 implemented in
shared/workflow/expression-authoring/__tests__/coreContracts.test.ts. The webhook payload context adapter now has contract coverage for turning a captured sample body into deterministic top-level expression roots and nested path options such asalert.id/device.hostname. - (2026-05-11) T121 implemented in
shared/workflow/expression-authoring/__tests__/coreContracts.test.ts. Captured webhook sample inference now covers nested objects, arrays, array-item path segments, and primitive type classification for integer/number/boolean/string fields. - (2026-05-11) T122 implemented in
shared/workflow/expression-authoring/__tests__/coreContracts.test.ts. Adapter edge-case coverage now verifies null bodies produce a minimalvalueroot, array bodies exposevalue[]paths, and empty object samples return empty roots/options without throwing. - (2026-05-11) T123 implemented in
server/src/test/unit/inboundWebhooks/InboundWebhookMappingField.test.ts. The inbound mapping field now exposes and tests the helper that converts captured-sample adapter paths intoExpressionTextAreafieldOptions, ensuring autocomplete receives paths and type hints from the webhook payload context adapter. - (2026-05-11) T130 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. The Settings webhooks shell now has source-level UI contract coverage for the tab container, inbound/outbound tab trigger IDs, tab values, and the inbound/outbound tab panel components. - (2026-05-11) T131 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Outbound regression coverage now verifies the outbound tab still mountsOutboundWebhooksSetupand that the existing outbound list/save/delete/test/rotate/retry/delivery/stat action paths remain wired in the refactored component. - (2026-05-11) T132 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. The inbound list view now has contract coverage for the required table shape: name/receiver URL, handler, last delivery, active state, and DataTable binding to inbound webhook rows. - (2026-05-11) T133 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. The inbound create/edit dialog now has contract coverage for the Identity section title/help and stable inputs for name, URL-safe slug normalization, and description. - (2026-05-11) T134 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Auth-section UI coverage now verifies the HMAC auth option and conditional signature-header input are present with stable IDs and state wiring. - (2026-05-11) T135 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Auth-section UI coverage now verifies the Bearer option renders a password token field, uses the saved-secret placeholder for existing configs, and writes back toidentityForm.bearerToken. - (2026-05-11) T136 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Auth-section UI coverage now verifies the IP allowlist option renders the CIDR textarea with stable ID, translated labels/placeholders, andidentityForm.ipCidrsstate wiring. - (2026-05-11) T137 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsxandserver/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. While adding coverage, a real UI gap was found: inbound create/update save was still disabled. The dialog now builds the snake_case upsert payload, callsupsertInboundWebhook, updates local list state, and displays the returned one-time secret only inrevealedInboundSecret, which is cleared on modal close. - (2026-05-11) T138 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsxandserver/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Saved non-IP inbound webhook configs now show a rotate-secret button that callsrotateInboundWebhookSecretand reuses the one-time secret modal for the replacement secret. - (2026-05-11) T139 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Idempotency-section UI coverage now verifies the header/jsonata source dropdown, value input label/placeholder switch, and duplicate-window seconds input are present and state-backed. - (2026-05-11) T140 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Handler-section UI coverage now verifies the handler type selector exposes direct-action/workflow options, updatesidentityForm.handlerType, and conditionally renders the direct-action or workflow panel. - (2026-05-11) T141 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Direct-action dropdown coverage now verifies actions are loaded fromlistInboundWebhookActions, mapped into selector options by registry name/display metadata, and rendered through theinbound-webhook-direct-actionselect. - (2026-05-11) T142 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Direct-action mapping coverage now verifies selected action definitions drive target-field rows, each row uses a stableinbound-webhook-mapping-*ID, and mappings are edited through the reusedInboundWebhookMappingFieldwith captured sample context. - (2026-05-11) T143 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Workflow selector coverage now verifies tenant workflows are loaded throughlistInboundWorkflowOptions, mapped into select options with workflow IDs/names, and rendered by theinbound-webhook-workflowselect. - (2026-05-11) T144 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Workflow handler UI coverage now verifies the envelope info card includes the documentedsource,body,headers,verified,delivery_id,idempotency_key, andreceived_atfields. - (2026-05-11) T145 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Sample-capture UI coverage now verifies the capture button is saved-webhook gated, callscaptureSamplePayload, writes returned capture/sample state into the dialog form, and shows the active capture window status. - (2026-05-11) T146 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Sample-tree UI coverage now verifies captured payloads are converted throughbuildWebhookPayloadExpressionPathOptions, empty state is handled, and path buttons render stable IDs plus the path text. - (2026-05-11) T147 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Sample-tree insertion coverage now verifies mapping fields set the focused target, path buttons are disabled until a field is focused, and clicking a path writes it intoidentityForm.fieldMapping[focusedMappingField]. - (2026-05-11) T148 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Active-toggle UI coverage now verifies saved configs callsetInboundWebhookActiveState, updateisActiveplusautoDisabledAtfrom the persisted result, and wire theinbound-webhook-activeswitch to that handler. - (2026-05-11) T149 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Auto-disable banner coverage now verifies the dialog renders translated warning copy whenidentityForm.autoDisabledAtis set and formats the disabled timestamp for display. - (2026-05-11) T160 implemented in
server/src/components/settings/security/AdminWebhooksSetup.tsxandserver/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. While adding coverage, a real UI gap was found: the delivery dialog paginated by webhook but lacked a dispatch-status filter. Addedinbound-webhook-delivery-status-filter, status state, and query wiring solistInboundDeliveriesreceives bothinboundWebhookIdand optionalstatus. - (2026-05-11) T161 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Delivery-detail drawer coverage now verifies selected delivery data drives response status, duration, handler error, filtered headers, request body, response body, and formatted JSON sections. - (2026-05-11) T162 implemented in
server/src/test/unit/inboundWebhooks/deliveryPersistence.test.ts. Replay persistence coverage now verifiescreateInboundDeliverywritesis_replay=trueandreplayed_from=<original delivery>when replay callers create the linked delivery row. - (2026-05-11) T163 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookReplay.source.contract.test.ts. Replay source coverage now verifiesreplayInboundDeliveryfetches the current active webhook config by tenant/webhook ID and passes that current row intodispatchAndRecordOutcome, so mapping/config changes are reflected on replay. - (2026-05-11) T164 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookReplay.source.contract.test.ts. Replay linkage source coverage now verifiesreplayInboundDeliverycreates the replay row withisReplay: true,replayedFrom: original.delivery_id, and returns the newly fetched replay delivery. - (2026-05-11) T165 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Synthetic-test UI coverage now verifies the dialog exposes custom JSON body and header editors, parses both, callssendInboundWebhookTestfor in-process dispatch, and opens the resulting delivery detail. - (2026-05-11) T170 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Inbound UI i18n coverage now scans the inbound component region for direct JSX English text nodes and verifies the component uses themsp/profiletranslation namespace plus inbound translation keys. - (2026-05-11) T171 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. UI reflection coverage now scans inbound static IDs for kebab-case form and verifies dynamic mapping, sample-path, and delivery-view IDs are generated from kebab-case prefixes. - (2026-05-11) T180 implemented in
server/src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts. Settings feature-flag coverage now verifiesinbound_webhooks_enableddefaults off, the active tab falls back to outbound if disabled, and inbound tab/content are only rendered behind the flag. - (2026-05-11) T181 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Receiver feature-flag coverage now verifies a disabledinbound_webhooks_enabledflag returns a bodyless 404 after tenant resolution and before tenant context, webhook lookup, delivery persistence, or dispatch. - (2026-05-11) T182 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. The receiver now has tenant-specific flag coverage: tenant A can proceed throughrunWithTenantand dispatch while tenant B, in the same mocked scenario, receives a bodyless 404 and never reaches lookup, delivery creation, or dispatch. - (2026-05-11) T190 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookTenantScoping.source.contract.test.ts. Added a source contract that enumerates everyinbound_webhookstable access in server actions plus receiver config lookup, requires tenant-scopedwhereclauses on read/update/delete paths, and verifies create paths insert the tenant key. - (2026-05-11) T191 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookTenantScoping.source.contract.test.ts. Extended the source contract to enumerate delivery table access in server actions, delivery persistence, and idempotency helpers; inserts must includetenant, direct lookups/updates must filter by tenant, and paginated list queries must flow through the sharedapplyFilterstenant guard. - (2026-05-11) T192 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Added a mockedgetInboundDeliverycross-tenant test: a tenant-B delivery row is not returned to the tenant-A auth context because the action queries by{ tenant: 'tenant-a', delivery_id }. - (2026-05-11) T193 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Tightened the mocked config lookup harness to honorwherecriteria and added a cross-tenantgetInboundWebhooktest proving tenant-A cannot read a tenant-B webhook config with the same requested id. - (2026-05-11) T200 implemented in
server/src/test/unit/inboundWebhooks/requestProcessor.test.ts. Added an executable acceptance-path contract for HMAC create-ticket webhooks: a signed JSON request reaches the direct-action dispatcher, returns{delivery_id}, and persists acreateTickethandler outcome with the created ticket id/number for the Settings delivery UI to display. - (2026-05-11) T201 implemented in
server/src/test/unit/inboundWebhooks/workflowDispatcher.test.ts. Added a workflow-handler acceptance contract verifying the dispatcher launches the published workflow withexecute=true, delivery-scoped trigger/execution keys, and returnsworkflow_run_id/version so delivery logs can link users to the visible workflow run. - (2026-05-11) T202 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts. Added a replay acceptance-path test: a failed original delivery is loaded from the log, replay creates a linked delivery with current webhook mapping, dispatch succeeds, and the returned delivery is markedisReplay=truewithreplayedFrompointing to the failed original. - (2026-05-11) T300 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts. TheGET /api/v1/inbound-webhooksroute test verifies a 200 response with{ data }and confirms the route delegates tolistInboundWebhooks(), preserving the tenant-scoped server-action path covered by T020/T190. - (2026-05-11) T301 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts. ThePOST /api/v1/inbound-webhooksroute test verifies create requests delegate the parsed JSON body toupsertInboundWebhook, return201, and expose the returned secret only as the one-timesecretresponse field. - (2026-05-11) T302 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts. TheGET /api/v1/inbound-webhooks/{id}route test verifies lookup by path id, 200{ data }response shape, and that raw secret material is absent from the returned config. - (2026-05-11) T303 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts. ThePUT /api/v1/inbound-webhooks/{id}route test verifies the parsed body is persisted throughupsertInboundWebhookwith the URL path id overriding any body id and that updates return{ data, secret:null }. - (2026-05-11) T304 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts. TheDELETE /api/v1/inbound-webhooks/{id}route test verifies deletion delegates todeleteInboundWebhook(id)and returns an empty204response. - (2026-05-11) T305 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts. The rotate-secret route test verifiesPOST /api/v1/inbound-webhooks/{id}/rotate-secretdelegates torotateInboundWebhookSecret(id)and returns the replacement as the one-timesecretresponse field. - (2026-05-11) T306 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts. The synthetic test route verifiesPOST /api/v1/inbound-webhooks/{id}/testparses{body, headers}, callssendInboundWebhookTest(id, input), and returns the created delivery with202. - (2026-05-11) T307-T313 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts. Added REST route coverage for capture enable/clear, delivery pagination/filtering, delivery detail ownership check, replay creation/linkage, and action discovery output. These tests verify the route layer delegates to the existing server actions without introducing a parallel auth or data access path. - (2026-05-11) T320-T328 implemented in
server/src/test/unit/api/inboundWebhooksOpenApi.contract.test.ts. Added generated CE/EE YAML checks for inbound webhook paths, receiver endpoint header/response documentation, config/auth/handler/envelope component assertions, and explicit management/action-discovery schema reference checks. - (2026-05-11) T329 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts. Added an authorization-failure route test that lets the mocked server action throw a403and verifies the REST layer returns that error instead of bypassing or replacing the server-action permission path. - (2026-05-11) T330 implemented in
server/src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.source.contract.test.ts. Added a source contract asserting every inbound webhook REST route imports@/lib/actions/inboundWebhookActionsand avoids direct DB/auth/RBAC imports, preserving tenant scoping parity with the server actions. - (2026-05-11) F043 implemented in
server/src/lib/inboundWebhooks/headerFilter.ts.filterInboundWebhookHeadersacceptsHeadersor plain records, lowercases persisted names, and stripsAuthorization,Cookie,Set-Cookie,Proxy-Authorization, andX-Api-Key. - (2026-05-11) F038 implemented in
server/src/lib/inboundWebhooks/idempotency.ts.extractInboundWebhookIdempotencyKeysupports case-insensitive header lookup fromHeadersor plain header records and returnsnullfor missing/blank keys. - (2026-05-11) F039 implemented in
server/src/lib/inboundWebhooks/idempotency.ts. JSONata idempotency sources evaluate directly against the request body via the workflow expression runtime and normalize non-null results to trimmed strings. - (2026-05-11) F040 implemented in
server/src/lib/inboundWebhooks/idempotency.ts.findDuplicateInboundDeliverychecks(tenant, inbound_webhook_id, idempotency_key)within the configured window and only treatspending,dispatched, or priorduplicaterows as dedup hits. - (2026-05-11) F041 implemented in
server/src/lib/inboundWebhooks/deliveryPersistence.ts.createInboundDeliveryinserts tenant-scoped rows before dispatch, filters persisted headers through F043, stores request bodies only forauth_status='verified', and supports replay linkage fields. - (2026-05-11) F033 implemented in
server/src/lib/inboundWebhooks/authVerifier.ts. HMAC-SHA256 verification uses configurable signature header names, supportssha256=<hex>or raw hex signatures, reads secrets from tenant secret storage, and compares with paddedcrypto.timingSafeEqual. - (2026-05-11) F034 implemented in
server/src/lib/inboundWebhooks/authVerifier.ts. Bearer auth readsAuthorization: Bearer <token>, loads the tenant secret by vault path metadata, and uses the same timing-safe comparison helper. - (2026-05-11) F035 implemented in
server/src/lib/inboundWebhooks/authVerifier.ts. IP allowlist auth supports exact IP strings and IPv4 CIDR ranges without adding a new dependency. IPv6 is currently exact-match only; add a CIDR library if IPv6 CIDR support becomes required. - (2026-05-11) F036 implemented in
server/src/lib/inboundWebhooks/authVerifier.ts. Path-token auth reads the configured query parameter (defaulttoken), loads the tenant token secret, and compares with the timing-safe helper. - (2026-05-11) F037 implemented in
server/src/lib/inboundWebhooks/responses.ts.unauthorizedInboundWebhookResponse()returns a bodyless401NextResponseto reuse for unknown tenant, unknown webhook, disabled webhook, and bad auth cases. - (2026-05-11) F031 implemented in
server/src/lib/inboundWebhooks/tenantResolver.ts. Uses existing@alga-psa/db.getTenantIdBySlugand caches positive/negative lookups for 60 seconds; tenant slugs are the existing 12-hex portal slug format derived from tenant UUIDs. - (2026-05-11) F032 implemented in
server/src/lib/inboundWebhooks/configLookup.ts.lookupInboundWebhookBySlug(knex, tenant, webhookSlug)queriesinbound_webhookswith bothtenantandslug, returning only receiver-needed fields; backed by F001 unique(tenant, slug)index. - (2026-05-11) F044 implemented in
server/src/lib/inboundWebhooks/sampleCapture.ts.captureInboundWebhookSampleIfRequestedstores the first verified body only whilesample_capture_expires_at > now, clears the capture window after storing, and leaves existing samples untouched until the admin explicitly re-captures/clears. - (2026-05-11) F042 implemented in
server/src/lib/inboundWebhooks/rateLimitConfig.ts. Inbound webhooks use the shared Redis token bucket with a distinctwebhook-innamespace, default to 600/min, and load per-webhook overrides frominbound_webhooks.rate_limit_per_minutescoped by tenant and webhook id. - (2026-05-11) F030 implemented with
server/src/app/api/inbound/[tenantSlug]/[webhookSlug]/route.tsplus sharedserver/src/lib/inboundWebhooks/requestProcessor.ts. The route supports POST/PUT/PATCH, gates on the inbound feature flag, resolves tenant slug, verifies auth, applies rate limit/idempotency, persists deliveries before dispatch, captures samples, and dispatches direct actions through the registry. Workflow dispatch still remains a later feature (F060). - (2026-05-11) Bundled integrations already in the codebase — the user-configurable system complements these, not replaces them:
- Tanium (EE) —
ee/server/src/lib/integrations/tanium/+taniumGatewayClient.ts. Outbound sync (devices → assets) + webhook. UsesingestNormalizedRmmDeviceSnapshot. - NinjaOne (EE) —
ee/server/src/lib/integrations/ninjaone/— has a full inbound webhook implementation (webhooks/webhookHandler.ts,webhooks/webhookRegistration.ts,alerts/alertProcessor.ts,alerts/ticketCreator.ts). The user-configurable inbound webhook system is the generic equivalent. - Tactical RMM (CE+EE) —
packages/integrations/src/lib/rmm/tacticalrmm/. Has webhook secret support (tacticalrmm_webhook_secret). - Xero, QBO (EE accounting) — outbound only currently.
- Entra ID (EE) — bidirectional user/group sync; Direct mode + CIPP mode.
- Microsoft Graph / Teams, Google — OAuth integrations for calendar/messaging.
- Tanium (EE) —
- (2026-05-11)
tenant_external_entity_mappingsis the canonical external-ID-to-Alga-entity lookup table. Created inserver/migrations/20250502173321_create_tenant_external_entity_mappings.cjs. Schema:(tenant_id, integration_type, alga_entity_type, alga_entity_id, external_entity_id, external_realm_id, sync_status, last_synced_at, metadata). Two unique constraints enforce one-to-one mapping. The plan uses this instead of addingexternal_refcolumns to individual entity tables. - (2026-05-11)
@alga-psa/integrations/lib/rmm/sharedAssetIngestionServiceexportsingestNormalizedRmmDeviceSnapshot— the shared device normalization path used by all RMM integrations. The inbound webhook asset action delegates here for RMM-shaped payloads. - (2026-05-11) Domain entity surface is large — 60+ mutable entities across
packages/*/src/actions/. v1 covers the high-leverage subset (tickets, clients, contacts, assets, invoices, time entries, project tasks, tags). Registry pattern means more can be added per package without touching inbound webhook core. - (2026-05-11) Outbound webhook system at
server/src/lib/webhooks/is mature: signing (X-Alga-Signaturev1), Redis queue, retries, auto-disable, SSRF guards, per-webhook rate limits. Most of this is dispatch-side and not directly reusable for inbound, but the delivery log + replay pattern is the right shape to copy. - (2026-05-11) OpenAPI infrastructure is in place:
- Central registry:
server/src/lib/api/openapi/registry.ts - Per-area route files:
server/src/lib/api/openapi/routes/*.ts— outbound webhooks already registered atroutes/webhooks.ts(model for inbound). - Generator:
sdk/scripts/generate-openapi.ts→ outputssdk/docs/openapi/alga-openapi.{ce,ee}.{yaml,json}. - Contract test pattern:
server/src/test/unit/api/projectTasksOpenApi.contract.test.ts. - Outbound webhook v1 API:
/api/v1/webhooks,/[id],/test,/verify,/templates,/events,/analytics,/[id]/health. Mirror this surface for inbound.
- Central registry:
- (2026-05-11) Existing Settings UI for outbound:
server/src/components/settings/security/AdminWebhooksSetup.tsx. Uses DataTable, Dialog, DropdownMenu primitives from@alga-psa/ui. Already i18n-ready. - (2026-05-11) Workflow editor's expression infra is rich:
ee/server/src/components/workflow-designer/expression-editor/ExpressionEditor.tsx— Monaco-based JSONata editor with completion, hover, diagnostics, signature help.ee/server/src/components/workflow-designer/mapping/ExpressionTextArea.tsx— single-line mapping editor (probably the right entry point for per-field rows).shared/workflow/expression-authoring/—pathDiscovery.ts,pathValidation.ts,validation.ts,context.ts, adapters for workflow/invoice contexts.shared/workflow/runtime/expressionEngine.ts— JSONata runtime evaluator. Use at request time to evaluate field mappings.
- (2026-05-11) Context adapter pattern: see
shared/workflow/expression-authoring/adapters/invoiceContextAdapter.tsfor a model of whatwebhookPayloadContextAdapter.tsshould look like. - (2026-05-11) CitusDB multi-tenant rules from CLAUDE.md:
- All queries WHERE on
tenant - JOIN conditions include
tenant - Composite PKs include
tenant app.current_tenantdoesn't propagate to all shards — usewithAuth/runWithTenant
- All queries WHERE on
- (2026-05-11) Migrations split: CE migrations in
server/migrations/, EE inee/server/migrations/. Inbound webhooks are CE (community-edition feature). - (2026-05-11) Authentication wrapper:
withAuthfrom@alga-psa/authis the canonical pattern. Sets tenant context via AsyncLocalStorage. - (2026-05-11) UI reflection: every interactive element needs a kebab-case
idattribute (e.g.id="inbound-webhook-create-button"). Mandatory per CLAUDE.md.
Commands / Runbooks
- (2026-05-11) Find workflow trigger entrypoint:
grep -rn "startWorkflow\|triggerWorkflow\|emit.*workflow" shared/workflow/runtime/ shared/workflow/services/- Check
services/workflow-worker/for the bus consumer.
- (2026-05-11) Test inbound endpoint locally:
curl -X POST http://localhost:3000/api/inbound/<tenant>/<slug> -H "X-Signature: ..." -d '{...}'
- (2026-05-11) Run migrations after changes:
npm run migrate
- (2026-05-11) Reset feature flag in PostHog UI or via API for testing.
- (2026-05-11) Type check after F014:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F015:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F016:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F017:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F018:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F019:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F021:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F022:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F050:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F054:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F061:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F070:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F043:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F038:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F040:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F041:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F033:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F037:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F031:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F032:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F044:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F042:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F030:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F020/F023 dispatcher work:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1010:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F053:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1011:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1012:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1013:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1020/F1021 client actions:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1030:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1040/F1041 asset actions:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1050/F1051/F1052 invoice actions:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1060:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1070:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1071:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F1080:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F060:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F072:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F080:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F081:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F082:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F083:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F084:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F085:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F086:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F087:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F088:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F089:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F090:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F091:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F092:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F093:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F094:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F095:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F096:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F100:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F101:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F200:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F201:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F202:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F203:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F204:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F205:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F206:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F207:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F208:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F209:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F210:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F211:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F212:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F213:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F214:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F215:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F216:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F217:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Type check after F218:
npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) OpenAPI generation after F220:
node sdk/scripts/generate-openapi.ts(failed: TypeScript ESM directory import without the repo loader)npm run openapi:generate --workspace=sdk -- --edition ce && npm run openapi:generate --workspace=sdk -- --edition ee(failed: npm matched nested SDK workspaces without that script)(cd sdk && npm run openapi:generate -- --edition ce && npm run openapi:generate -- --edition ee)
- (2026-05-11) Test/typecheck after F221:
(cd server && npm run test -- src/test/unit/api/inboundWebhooksOpenApi.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after F222:
(cd server && npm run test -- src/test/unit/api/inboundWebhooksOpenApi.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Audit after F223:
rg -n "from '@/lib/actions/inboundWebhookActions'|withAuth|hasPermission|createTenantKnex" server/src/app/api/v1/inbound-webhooks -g'route.ts'find server/src/app/api/v1/inbound-webhooks -name route.ts -print | sort
- (2026-05-11) Test/typecheck after T001:
(cd server && npm run test -- src/test/unit/migrations/inboundWebhookMigrations.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T002:
(cd server && npm run test -- src/test/unit/migrations/inboundWebhookMigrations.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T003:
(cd server && npm run test -- src/test/unit/migrations/inboundWebhookMigrations.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T004:
(cd server && npm run test -- src/test/unit/inboundWebhooks/externalEntityMappings.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T004a:
(cd server && npm run test -- src/test/unit/inboundWebhooks/externalEntityMappings.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T004b:
(cd server && npm run test -- src/test/unit/inboundWebhooks/schemas.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T005:
(cd server && npm run test -- src/test/unit/migrations/inboundWebhookMigrations.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T006:
(cd server && npm run test -- src/test/unit/migrations/inboundWebhookPermissions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T007:
(cd server && npm run test -- src/test/unit/migrations/inboundWebhookPermissions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T010:
(cd server && npm run test -- src/test/unit/inboundWebhooks/schemas.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T011:
(cd server && npm run test -- src/test/unit/inboundWebhooks/schemas.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T012:
(cd server && npm run test -- src/test/unit/inboundWebhooks/schemas.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T013:
(cd server && npm run test -- src/test/unit/inboundWebhooks/schemas.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T014:
(cd server && npm run test -- src/test/unit/inboundWebhooks/schemas.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T020:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T021:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T022:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T023:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T024:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T025:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T026:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T027:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T028:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T030:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T031:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T032:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T033:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T034:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T035:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T036:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T037:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T038:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T039:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T040:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T041:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T042:
(cd server && npm run test -- src/test/unit/inboundWebhooks/authVerifier.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T043:
(cd server && npm run test -- src/test/unit/inboundWebhooks/authVerifier.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T044:
(cd server && npm run test -- src/test/unit/inboundWebhooks/headerFilter.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T050:
(cd server && npm run test -- src/test/unit/inboundWebhooks/idempotency.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T051:
(cd server && npm run test -- src/test/unit/inboundWebhooks/idempotency.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T052:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T053:
(cd server && npm run test -- src/test/unit/inboundWebhooks/idempotency.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T054:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T055:
(cd server && npm run test -- src/test/unit/inboundWebhooks/idempotency.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T060:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T061:
(cd server && npm run test -- src/test/unit/inboundWebhooks/deliveryPersistence.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T062:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T070:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T071:
(cd server && npm run test -- src/test/unit/inboundWebhooks/rateLimitConfig.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T072:
(cd server && npm run test -- src/test/unit/inboundWebhooks/rateLimitConfig.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T080:
(cd server && npm run test -- src/test/unit/inboundWebhooks/sampleCapture.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T081:
(cd server && npm run test -- src/test/unit/inboundWebhooks/sampleCapture.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T082:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T090:
(cd server && npm run test -- src/test/unit/inboundWebhooks/actionRegistry.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T091:
(cd server && npm run test -- src/test/unit/inboundWebhooks/actionRegistry.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T092:
(cd server && npm run test -- src/test/unit/inboundWebhooks/actionRegistry.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T093:
(cd server && npm run test -- src/test/unit/inboundWebhooks/mappingEvaluator.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T094:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T095:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T096:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1010:
(cd server && npm run test -- src/test/unit/inboundWebhooks/ticketInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1011:
(cd server && npm run test -- src/test/unit/inboundWebhooks/ticketInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1012:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1013:
(cd server && npm run test -- src/test/unit/inboundWebhooks/ticketInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1014:
(cd server && npm run test -- src/test/unit/inboundWebhooks/ticketInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1015:
(cd server && npm run test -- src/test/unit/inboundWebhooks/ticketInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1016:
(cd server && npm run test -- src/test/unit/inboundWebhooks/ticketInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1020:
(cd server && npm run test -- src/test/unit/inboundWebhooks/clientInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1021:
(cd server && npm run test -- src/test/unit/inboundWebhooks/clientInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1022:
(cd server && npm run test -- src/test/unit/inboundWebhooks/clientInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1030:
(cd server && npm run test -- src/test/unit/inboundWebhooks/clientInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1031:
(cd server && npm run test -- src/test/unit/inboundWebhooks/clientInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1040:
(cd server && npm run test -- src/test/unit/inboundWebhooks/assetInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1041:
(cd server && npm run test -- src/test/unit/inboundWebhooks/assetInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1042:
(cd server && npm run test -- src/test/unit/inboundWebhooks/assetInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1050:
(cd server && npm run test -- src/test/unit/inboundWebhooks/invoiceInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1051:
(cd server && npm run test -- src/test/unit/inboundWebhooks/invoiceInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1052:
(cd server && npm run test -- src/test/unit/inboundWebhooks/invoiceInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1053:
(cd server && npm run test -- src/test/unit/inboundWebhooks/invoiceInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1060:
(cd server && npm run test -- src/test/unit/inboundWebhooks/timeEntryInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1070:
(cd server && npm run test -- src/test/unit/inboundWebhooks/projectTaskInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1071:
(cd server && npm run test -- src/test/unit/inboundWebhooks/projectTaskInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1080:
(cd server && npm run test -- src/test/unit/inboundWebhooks/tagInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1081:
(cd server && npm run test -- src/test/unit/inboundWebhooks/tagInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T1082:
(cd server && npm run test -- src/test/unit/inboundWebhooks/tagInboundActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T110:
(cd server && npm run test -- src/test/unit/inboundWebhooks/workflowDispatcher.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T111:
(cd server && npm run test -- src/test/unit/inboundWebhooks/workflowEnvelope.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T112:
(cd server && npm run test -- src/test/unit/inboundWebhooks/workflowDispatcher.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T113:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T114:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T115:
(cd server && npm run test -- src/test/unit/inboundWebhooks/workflowDispatcher.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T120:
npx vitest run --config shared/vitest.config.ts shared/workflow/expression-authoring/__tests__/coreContracts.test.tsnpx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T121:
npx vitest run --config shared/vitest.config.ts shared/workflow/expression-authoring/__tests__/coreContracts.test.tsnpx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T122:
npx vitest run --config shared/vitest.config.ts shared/workflow/expression-authoring/__tests__/coreContracts.test.tsnpx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T123:
(cd server && npm run test -- src/test/unit/inboundWebhooks/InboundWebhookMappingField.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T130:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T131:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T132:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T133:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T134:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T135:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T136:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T137:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T138:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T139:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T140:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T141:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T142:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T143:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T144:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T145:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T146:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T147:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T148:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T149:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T160:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T161:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T162:
(cd server && npm run test -- src/test/unit/inboundWebhooks/deliveryPersistence.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T163:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookReplay.source.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T164:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookReplay.source.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T165:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T170:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T171:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T180:
(cd server && npm run test -- src/test/unit/inboundWebhooks/AdminWebhooksSetup.ui.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T181:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T182:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T190:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookTenantScoping.source.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T191:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookTenantScoping.source.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T192:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T193:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T200:
(cd server && npm run test -- src/test/unit/inboundWebhooks/requestProcessor.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T201:
(cd server && npm run test -- src/test/unit/inboundWebhooks/workflowDispatcher.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T202:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookActions.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T300:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T301:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T302:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T303:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T304:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T305:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T306:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T307-T313:
(cd server && npm run test -- src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
- (2026-05-11) Test/typecheck after T320-T330:
(cd server && npm run test -- src/test/unit/api/inboundWebhooksOpenApi.contract.test.ts src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.test.ts src/test/unit/inboundWebhooks/inboundWebhookRestApiRoutes.source.contract.test.ts)npx tsc -p server/tsconfig.json --noEmit --pretty false
Links / References
- PRD:
./PRD.md - Features:
./features.json - Tests:
./tests.json - Commit groups (for /loop):
./COMMIT_GROUPS.md - Outbound webhook system:
server/src/lib/webhooks/ - Outbound subscriber:
server/src/lib/eventBus/subscribers/webhookSubscriber.ts - Outbound UI:
server/src/components/settings/security/AdminWebhooksSetup.tsx - Outbound migrations:
server/migrations/20260505140000_create_webhook_tables.cjs - OpenAPI registry:
server/src/lib/api/openapi/registry.ts - OpenAPI route file (outbound, model for inbound):
server/src/lib/api/openapi/routes/webhooks.ts - OpenAPI generator:
sdk/scripts/generate-openapi.ts - OpenAPI output:
sdk/docs/openapi/alga-openapi.{ce,ee}.{yaml,json} - Contract test pattern:
server/src/test/unit/api/projectTasksOpenApi.contract.test.ts - Workflow expression editor:
ee/server/src/components/workflow-designer/expression-editor/ - Workflow mapping components:
ee/server/src/components/workflow-designer/mapping/ - Expression-authoring core:
shared/workflow/expression-authoring/ - JSONata runtime:
shared/workflow/runtime/expressionEngine.ts - Server action auth pattern: CLAUDE.md → "Server Action Authentication Pattern"
- Citus multi-tenant rules: CLAUDE.md → "Critical Multi-Tenant Rules (CitusDB)"
Open Questions
- Workflow trigger entrypoint: Does the engine expose a synchronous
startWorkflow(workflowId, input)API, or must inbound dispatch via event bus? Reference NinjaOnealertProcessor.ts— it may already invoke workflows and would show the pattern. Spike during F060. - Tenant slug source: Tenants need a URL-safe identifier for
/api/inbound/[tenantSlug]/.... Does an appropriate column exist (e.g.tenants.url_slug)? If not, add one in a prerequisite migration. - Reserved integration_type collision: Bundled integrations write to
tenant_external_entity_mappingswithintegration_typelike'ninjaone','tactical_rmm','tanium','xero','qbo','entra'. User webhook slugs must not collide. Solutions: (a) maintain a reserved-name list and reject; (b) prefix user slugs withuser:namespace; (c) use a separate columnintegration_source(system vs user). Decide before F003. Lean toward (a) — explicit reject with clear error. - Bootstrap action loading: Server start must load all package action contributions before first inbound request. Static imports from
server/src/lib/inboundWebhooks/actions/bootstrap.tsis simplest; dynamic discovery supports plugins later but adds complexity. - Missing idempotency key behavior: When idempotency_source is configured but the request doesn't include the key — accept (single dispatch, no dedup) or reject (400)? Lean toward accept-with-warning logged.
- Sample payload PII: Captured samples may contain sensitive data. Plan: mark as sensitive, restrict access to webhook owner, no auto-redaction in v1. Revisit if security raises concerns.
- Replay against changed config: Documented behavior is "uses current config." Should the delivery detail show a banner if the mapping changed since the original delivery? Nice-to-have, not v1.
- Delivery retention: PRD says 30 days for raw body. Confirm with ops/legal that this is acceptable for financial/device data.
- Rate limit defaults: 600/min per webhook is a guess. Review against expected RMM alert volumes.
upsertContactByExternalIdclient linkage: Should the action require aclient_external_idmapped (with lookup) or acceptclient_iddirectly, or both? Confirm at design time.- Consolidation with bundled integrations (v2 candidate): Should NinjaOne / Tactical RMM eventually consume the same user-configurable webhook plumbing with their secrets/normalizers preset? Out of scope for v1.