Skip to content

fix(stream): the subscription script ignored Stream's product scoping - #1153

Merged
teetangh merged 1 commit into
devfrom
fix/stream-webhook-product-scope
Aug 13, 2026
Merged

fix(stream): the subscription script ignored Stream's product scoping#1153
teetangh merged 1 commit into
devfrom
fix/stream-webhook-product-scope

Conversation

@teetangh

Copy link
Copy Markdown
Contributor

Found by running the script from #1141 against production, which is the only place it could have been found. Part of #1134.

What happened

--apply was refused outright:

UpdateApp failed with error: "invalid event types for hook 44a1d716-…:
event types [message.flagged user.flagged] do not belong to product 'video'"

Stream partitions event types by product, and a hook may only carry events from its own. This app has exactly one hook, scoped to video. The two chat-moderation events can never live on it, no matter how many times the script runs.

Why the old script made this worse rather than surfacing it

It had no concept of product. Its dry run reported all five unsubscribed events as plainly "missing", which reads as one write away from fixed:

MISSING (5):
  + call.session_participant_joined
  + call.session_participant_left
  + call.session_started
  + message.flagged        ← impossible on this hook
  + user.flagged           ← impossible on this hook

And because updateAppSettings rejects the payload atomically, the two impossible events cost the three achievable ones in the same write. Nothing landed. The script would have kept reporting the same five as missing forever, with the operator reasonably concluding the write simply had not been run.

What it does now

Each hook is offered only the events its product permits, and anything left over is reported as a configuration gap rather than a pending write:

hook 44a1d716-…  enabled=true  product=video
  subscribed: 9
  ✅ already covers every handled video event

⚠️  2 handled event(s) have NO hook that may carry them.

  product 'chat' — no hook on this app is scoped to it:
    · message.flagged
    · user.flagged
  Create a 'chat' webhook in the Stream dashboard pointing at
  <origin>/api/stream/webhooks, then re-run.

The script deliberately does not create the missing hook. Creating one decides a public URL and starts real deliveries against it, so it belongs to a human in the dashboard — the same reasoning the existing "no webhook configured at all" branch already applies.

A hook with no product set is treated as unconstrained rather than skipped: the field is optional in the SDK, and refusing to widen a hook we cannot classify would be worse than letting Stream reject it with a precise message.

Verified in production

The video hook went 6 → 9 events. call.session_participant_joined, call.session_participant_left and call.session_started now deliver, so MeetingAttendance can be written for the first time — it has been sitting at zero rows, which is what made detect-consultant-no-shows a job running daily against an empty table.

Chat moderation stays dead until a chat hook exists. That is now stated out loud rather than implied to be one run away.

7 suites pass, tsc and eslint clean.

Operator follow-up

Create a second webhook in the Stream dashboard with product chat, pointing at https://familiarisenow.com/api/stream/webhooks, then re-run the script to subscribe user.flagged and message.flagged.

…#1134)

Applied against the live app and it was refused:

  UpdateApp failed: invalid event types for hook 44a1d716-…:
  event types [message.flagged user.flagged] do not belong to product 'video'

Stream partitions event types by product and a hook may only carry its own.
The app has exactly one hook, scoped to `video`. The two chat-moderation
events can never live on it.

The script had no concept of `product`, so it reported all five unsubscribed
events as plainly "missing" — which reads as one write away from fixed. Two
of them were impossible. Worse, `updateAppSettings` rejects the whole payload
atomically, so the single impossible event cost the three achievable ones in
the same write and nothing landed at all.

Now each hook is offered only what its product permits, and any handled event
with no hook to carry it is reported as a configuration gap rather than a
pending write. The script does not create the missing hook: that decides a
public URL and starts real deliveries, so it belongs to a human in the
dashboard, exactly as the existing "no webhook at all" branch already does.

Verified against production. The video hook went 6 -> 9 events, so
call.session_participant_joined / _left and call.session_started now deliver
and MeetingAttendance can finally be written. Chat moderation stays dead
until a 'chat' hook exists — which the script now says out loud instead of
implying it is one run away.

Part of #1134
@netlify

netlify Bot commented Aug 13, 2026

Copy link
Copy Markdown

Deploy Preview for familiarise ready!

Name Link
🔨 Latest commit e4b1869
🔍 Latest deploy log https://app.netlify.com/projects/familiarise/deploys/6a7deba92d39a9000974e062
😎 Deploy Preview https://deploy-preview-1153--familiarise.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 31 (🔴 down 30 from production)
Accessibility: 90 (no change from production)
Best Practices: 83 (no change from production)
SEO: 82 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@teetangh, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 111 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a3caca6c-1381-49c0-b7ab-4a975de9fd26

📥 Commits

Reviewing files that changed from the base of the PR and between 4d60f75 and e4b1869.

📒 Files selected for processing (1)
  • scripts/stream/ensure-webhook-subscription.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

@teetangh
teetangh merged commit fa5f224 into dev Aug 13, 2026
8 checks passed
@teetangh
teetangh deleted the fix/stream-webhook-product-scope branch August 13, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant