Skip to content

feat(webhook): add WebhookTrigger class for Workflow Builder triggers - #2615

Merged
zimeg merged 25 commits into
mainfrom
eden/webhook-trigger
Jul 8, 2026
Merged

feat(webhook): add WebhookTrigger class for Workflow Builder triggers#2615
zimeg merged 25 commits into
mainfrom
eden/webhook-trigger

Conversation

@zimeg

@zimeg zimeg commented Jun 2, 2026

Copy link
Copy Markdown
Member

Summary

This pull request adds a minimal WebhookTrigger class to @slack/webhook for Workflow Builder webhook triggers.

The package previously only supported incoming webhooks (IncomingWebhook). Workflow Builder triggers accept arbitrary JSON payloads and return JSON responses, so consumers like slack-github-action currently fall back to raw fetch.

The implementation is intentionally small and mirrors IncomingWebhook:

  • Constructor takes a URL and optional defaults (timeout, agent); rejects a missing or empty URL.
  • send(payload?) POSTs the payload (defaults to {}) and resolves to the parsed JSON response body (WebhookTriggerResult, typed { ok, error? }); HTTP and request failures reject.
  • Reuses the existing error infrastructure and User-Agent instrumentation, adding WebhookTrigger-named error types.

Notes for reviewers

  • URL guard: the constructor uses a falsy check (!url), so it rejects an empty string as well as undefined. This intentionally differs from IncomingWebhook, which only guards undefined.
  • Result typing: WebhookTriggerResult is typed closed as { ok: boolean; error?: string } — matching what the trigger endpoint returns ({ ok: true } or { ok: false, error }). Widening this later is backward-compatible; tightening a released open type would not be.

Preview

🔗 https://github.com/slackapi/node-slack-sdk/tree/eden/webhook-trigger/packages/webhook#trigger-a-workflow-builder-workflow

const { WebhookTrigger } = require('@slack/webhook');
const url = process.env.SLACK_WEBHOOK_TRIGGER_URL;

const trigger = new WebhookTrigger(url);

(async () => {
  // Keys should match the variables your workflow expects
  const result = await trigger.send({
    customer_name: 'Ada Lovelace',
    order_id: '1024',
  });
})();

Requirements

Add a new WebhookTrigger class that mirrors IncomingWebhook but handles
Workflow Builder webhook triggers which return JSON responses with
arbitrary payloads (vs plain text "ok" from incoming webhooks).

- Constructor takes URL + defaults (timeout, agent) — same pattern
- send() accepts arbitrary key-value payload, returns { ok, body }
- Reuses existing error infrastructure and User-Agent instrumentation
- Enables consumers like slack-github-action to use the SDK instead
  of raw fetch for WFB triggers

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
@changeset-bot

changeset-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5ddeb03

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@slack/webhook Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codecov

codecov Bot commented Jun 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.45794% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.97%. Comparing base (b0e335b) to head (5ddeb03).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2615      +/-   ##
==========================================
+ Coverage   88.90%   88.97%   +0.06%     
==========================================
  Files          63       64       +1     
  Lines       10256    10363     +107     
  Branches      452      467      +15     
==========================================
+ Hits         9118     9220     +102     
- Misses       1117     1122       +5     
  Partials       21       21              
Flag Coverage Δ
cli-hooks 88.97% <93.45%> (+0.06%) ⬆️
cli-test 88.97% <93.45%> (+0.06%) ⬆️
logger 88.97% <93.45%> (+0.06%) ⬆️
oauth 88.97% <93.45%> (+0.06%) ⬆️
socket-mode 88.97% <93.45%> (+0.06%) ⬆️
web-api 88.97% <93.45%> (+0.06%) ⬆️
webhook 88.97% <93.45%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

zimeg and others added 2 commits June 2, 2026 14:20
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Workflow Builder webhook trigger inputs are always string values.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>

@WilliamBergamin WilliamBergamin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gave this a quick look and it looks good 💯

if we need to dry this up in the future we can look into it at that time

# Conflicts:
#	packages/webhook/package.json
#	packages/webhook/src/index.ts
@zimeg zimeg added enhancement M-T: A feature request for new functionality pkg:webhook applies to `@slack/webhook` semver:minor labels Jun 30, 2026
@zimeg zimeg self-assigned this Jun 30, 2026
@zimeg zimeg added the javascript Pull requests that update Javascript code label Jun 30, 2026
@zimeg zimeg modified the milestones: webhook@next, webhook@7.1.0 Jun 30, 2026
zimeg and others added 12 commits July 3, 2026 10:05
Allow send() to be called with no arguments, POSTing an empty body.
send({}) continues to work unchanged.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Use a falsy check so an empty-string URL is rejected up front rather
than failing later at request time. Adds a guard test.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
…s test

Add a test that a 401 with { ok: false, error: 'invalid_auth' } rejects
with an HTTPError exposing the response status and body. Remove the
success test asserting workflow_run_id so success cases only assert
result.ok === true.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
WebhookTrigger throws the same coded errors as IncomingWebhook but had
no correspondingly named types. Add WebhookTriggerSendError /
WebhookTriggerHTTPError / WebhookTriggerRequestError aliases and export
them so consumers have properly-named errors to catch.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
…source

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Coerce result.ok from body.ok instead of defaulting a missing field to
true, so only an explicit ok:true reports success.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>

@zimeg zimeg left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗣️ Words I think for the readers and reviewers!

}

export type IncomingWebhookSendError = IncomingWebhookRequestError | IncomingWebhookHTTPError;
export type WebhookTriggerSendError = WebhookTriggerRequestError | WebhookTriggerHTTPError;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ note: The error handling is shared between webhook techniques but so we export these values under a similar namespace.

Comment thread packages/webhook/src/WebhookTrigger.ts Outdated
* Processes an HTTP response into a WebhookTriggerResult.
*/
private buildResult(response: AxiosResponse): WebhookTriggerResult {
const body = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔬 note: This feels defensive to me and might be removed as response is unpacked to match standard "fetch"!

@zimeg
zimeg marked this pull request as ready for review July 3, 2026 19:45
@zimeg
zimeg requested a review from a team as a code owner July 3, 2026 19:45

@WilliamBergamin WilliamBergamin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work 💯

Left one comment on test coverage but its possible my comment is redundent

code: ErrorCode.RequestError;
original: Error;
}
export type WebhookTriggerRequestError = IncomingWebhookRequestError;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Safe move 🧠 I like it 💯

});
});

describe('User-Agent header', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 💯

should we also add a tests that covers using addAppMetadata to expand the user-agent value?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WilliamBergamin Thanks for calling this out! We're introducing a test in 3bed3dc for this.

* A client for Slack's Workflow Builder webhook triggers
* @see {@link https://slack.com/help/articles/360041352714-Build-a-workflow--Create-a-workflow-that-starts-outside-of-Slack}
*/
export class WebhookTrigger {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paise for mirroring existing behaviors 💯

zimeg and others added 8 commits July 7, 2026 13:54
Drop the { ok, body } wrapper so send() resolves to the parsed response
body itself, exposing ok/error without nesting or a duplicated ok field.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Assert that metadata added via addAppMetadata is sent in the User-Agent
header on a WebhookTrigger request, alongside the base webhook agent.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
send() now returns response.data directly; the string-vs-object parse
branch was dead code since axios already parses JSON responses. Removes
the now-unused AxiosResponse import. Simplifies the eventual fetch swap.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
The trigger endpoint responds with { ok: true } or { ok: false, error },
so type the result closed rather than an open index signature. Widening
later is backward-compatible; tightening a released `any` would not be.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
The package now covers both Incoming Webhooks and Workflow Builder
triggers, so the narrower "Incoming Webhooks" title is outdated.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>

@zimeg zimeg left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔏 A few more comments of confidence!

});
});

describe('User-Agent header', () => {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WilliamBergamin Thanks for calling this out! We're introducing a test in 3bed3dc for this.

Comment on lines +61 to +76
public async send(payload: WebhookTriggerSendArguments = {}): Promise<WebhookTriggerResult> {
try {
const response = await this.axios.post(this.url, payload);
return response.data;
// biome-ignore lint/suspicious/noExplicitAny: errors can be anything
} catch (error: any) {
if (error.response !== undefined) {
throw httpErrorWithOriginal(error);
}
if (error.request !== undefined) {
throw requestErrorWithOriginal(error);
}
throw error;
}
}
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👁️‍🗨️ note: Our return inlines the response values without more change to mirror implementations of the @slack/web-api package a bit more!

Comment on lines +91 to +94
export interface WebhookTriggerResult {
ok: boolean;
error?: string;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪬 note: These values are more strict now too!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good 💯

If we need to add more fields in the future we can

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📚 note: I hoped to generalize this package README for now with added information but think we might revise this ongoing?

@zimeg zimeg removed the javascript Pull requests that update Javascript code label Jul 8, 2026

@WilliamBergamin WilliamBergamin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ship it 🚀

Comment on lines +91 to +94
export interface WebhookTriggerResult {
ok: boolean;
error?: string;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good 💯

If we need to add more fields in the future we can

@zimeg

zimeg commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

@WilliamBergamin So exciting! Thanks for sharing big ideas and kind thoughts toward this 🧠 💡

@zimeg
zimeg merged commit a5bd5c8 into main Jul 8, 2026
14 checks passed
@zimeg
zimeg deleted the eden/webhook-trigger branch July 8, 2026 17:48
WilliamBergamin added a commit that referenced this pull request Jul 9, 2026
Bring the new @slack/webhook features from main into v8:
- WebhookTrigger class for Workflow Builder triggers (#2615)
- opt-in retries for IncomingWebhook and WebhookTrigger (#2641)

Re-expressed on v8's fetch + Error-subclass architecture (main built them
on axios + error-factory functions). Non-webhook release churn from main's
parallel 7.x line (docs, web-api version bumps, CHANGELOGs) resolved to v8.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement M-T: A feature request for new functionality pkg:webhook applies to `@slack/webhook` semver:minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants