create-hec-event-integration

Use when adding a new HEC (HTTP Event Collector) event integration to the Bitwarden web client. Implements the Splunk token authentication model (Bearer token…

npx skills add https://github.com/bitwarden/clients --skill create-hec-event-integration

Create HEC Event Integration (Token Auth)

Step 1 - Prompts

Ask these questions one at a time — wait for each answer before proceeding.

Prompt 1 — Service name: "What is the service name for this integration?" (e.g. Splunk, CrowdStrike, Panther)

Use the answer as <ServiceName> throughout. The string value in the constant must exactly match what you use as the card's name in Step 4 — a mismatch silently saves the config with the wrong service name.

Prompt 2 — Authentication: "How is this integration authenticated?" (e.g. Token, API key)

  • If Token — continue with the steps below.
  • If anything else — stop and inform the user: "This skill currently only supports token-based authentication. Support for other authentication methods hasn't been added yet."

Prompt 3 — Logos: "Do you have the integration logo(s) ready to provide?"

  • If yes — ask for the light-mode SVG file path, and optionally a dark-mode SVG path. Copy both to apps/web/src/images/integrations/ using the naming convention logo-<service-name-kebab>-color.svg and logo-<service-name-kebab>-darkmode.svg. Use those filenames in Step 4.
  • If no — use placeholder paths in Step 4 and add a // TODO: add logo before shipping comment.

Step 2 — Add service name constant

File: bitwarden_license/bit-common/src/dirt/organization-integrations/models/organization-integration-service-type.ts

Add to OrganizationIntegrationServiceName:

export const OrganizationIntegrationServiceName = Object.freeze({
  CrowdStrike: "CrowdStrike",
  Datadog: "Datadog",
  Huntress: "Huntress",
  <ServiceName>: "<ServiceName>", // ← add here
} as const);

Step 3 — Add feature flag

File: libs/common/src/enums/feature-flag.enum.ts

Add the enum entry and its default. The enum key is PascalCase; the string value is kebab-case (e.g. CrowdStrike → crowdstrike, Sumo Logic → sumo-logic):

// In the FeatureFlag enum:
EventManagementFor<ServiceName> = "event-management-for-<service-name-kebab>",

// In the defaultFlags object:
[FeatureFlag.EventManagementFor<ServiceName>]: FALSE,

Example for Panther:

EventManagementForPanther = "event-management-for-panther",
[FeatureFlag.EventManagementForPanther]: FALSE,

Step 4 — Register the card behind the feature flag

File: bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.resolver.ts

If logos were provided, copy them to apps/web/src/images/integrations/ first, then use the actual filenames below. If not, use the placeholder paths with the TODO comment:

const <serviceName>FeatureEnabled = await firstValueFrom(
  this.configService.getFeatureFlag$(FeatureFlag.EventManagementFor<ServiceName>),
);

if (<serviceName>FeatureEnabled) {
  integrations.push({
    name: OrganizationIntegrationServiceName.<ServiceName>, // must match Step 1 exactly
    linkURL: "https://bitwarden.com/help/<service-name>-siem/",
    image: "../../../../../../../images/integrations/logo-<service-name>-color.svg", // TODO: add logo before shipping (if not yet provided)
    imageDarkMode: "../../../../../../../images/integrations/logo-<service-name>-darkmode.svg", // TODO: add logo before shipping (omit if no dark mode variant)
    type: IntegrationType.EVENT,
    canSetupConnection: true,
    integrationType: OrganizationIntegrationType.Hec,
  });
}

No changes needed to IntegrationCardComponent — new HEC services fall into the existing else branch, which calls openHecConnectDialog → saveHec → deleteHec. These methods already call buildHecConfiguration and buildHecTemplate using the card's name as the service name.

Step 5 — Add tests

File: bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts

Add one it block inside the existing describe("buildHecConfiguration", ...) block, and one inside describe("buildHecTemplate", ...). Use typed property access — do not use JSON.parse:

// Inside describe("buildHecConfiguration", ...)
it("should work with <ServiceName> service name", () => {
  const config = OrgIntegrationBuilder.buildHecConfiguration(
    "https://test.<servicename>.com/hec",
    "test-token",
    OrganizationIntegrationServiceName.<ServiceName>,
  );

  expect(config).toBeInstanceOf(HecConfiguration);
  expect((config as HecConfiguration).uri).toBe("https://test.<servicename>.com/hec");
  expect((config as HecConfiguration).scheme).toBe("Bearer");
  expect((config as HecConfiguration).token).toBe("test-token");
  expect(config.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);
});

// Inside describe("buildHecTemplate", ...)
it("should work with <ServiceName> service name", () => {
  const template = OrgIntegrationBuilder.buildHecTemplate(
    "test-index",
    OrganizationIntegrationServiceName.<ServiceName>,
  );

  expect(template).toBeInstanceOf(HecTemplate);
  expect((template as HecTemplate).index).toBe("test-index");
  expect(template.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);
});

Step 6 — Run unit tests

Run the unit tests for the spec file and confirm they all pass before finishing:

npx jest bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts

All tests must pass. If any fail, fix them before proceeding.

Common Mistakes

MistakeFix
name in card doesn't match OrganizationIntegrationServiceName valueThey must be identical strings — saveHec() casts the name directly
Feature flag default not set to FALSEAlways add the default entry in defaultFlags; new flags without a default will not work correctly
Kebab-case mismatch in flag stringConvert consistently: lowercase, spaces → hyphens
Adding a new OrganizationIntegrationTypeNot needed — all HEC services share OrganizationIntegrationType.Hec
Creating a new config/template classNot needed — HecConfiguration and HecTemplate handle all HEC services
Referencing an image path without copying the fileCopy SVGs to apps/web/src/images/integrations/ first; if logos aren't ready, leave the TODO comment

More skills from bitwarden

figma-to-angular
bitwarden
This skill turns a Figma design spec into a fully implemented Angular component with Storybook stories in the Bitwarden Clients monorepo. The output should match the design visually while following all codebase conventions.
force-multiplier
bitwarden
Apply one intent across many targets at once — a fleet of repositories across the Bitwarden ecosystem, or many projects inside a monorepo — as N consistent,…
analyzing-git-sessions
bitwarden
Analyzes git commits and changes within a timeframe or commit range, providing structured summaries for code review, retrospectives, work logs, or session…
coordinating-cross-team-breakdown
bitwarden
Coordinate cross-team review and signoff for a Bitwarden Tech Breakdown. Use when identifying affected teams, building the Part 3 signoff table, chasing…
assessing-jira-issue-relevance
bitwarden
Use when the user provides a single Jira issue key and asks whether it is still relevant, still applicable, still pending, still a bug, has been fixed, or can…
assessing-test-coverage
bitwarden
Use when determining what test coverage ALREADY exists for a specific change (a PR, Jira key, Tech Breakdown doc, Testmo CSV, changed paths, or named…
retrospecting
bitwarden
Performs comprehensive analysis of Claude Code sessions, examining git history, conversation logs, code changes, and gathering user feedback to generate…
reviewing-incremental-changes
bitwarden
Use this skill when re-reviewing a PR that already has comments or when responding to developer changes after initial review. Apply when PR threads exist or…