Skip to content

Establish webhooks journal example - #1

Merged
zprobs merged 3 commits into
mainfrom
establish-webhooks-journal-example
Oct 20, 2026
Merged

zprobs merged 3 commits into
mainfrom
establish-webhooks-journal-example

Conversation

@zprobs

@zprobs zprobs commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Establishes the webhooks journal example app for onboarding purposes
  • See the README for setup instructions and further reading

Express + TypeScript + Prisma backend with React frontend for
demonstrating HubSpot Webhooks Journal API integration patterns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@zprobs
zprobs force-pushed the establish-webhooks-journal-example branch from 76f5a50 to 3dafec8 Compare July 27, 2026 19:56
@zprobs
zprobs requested a review from zman81988 July 28, 2026 14:13
@zaradford

Copy link
Copy Markdown

Hi @zprobs, @zman81988 asked me to take a look at this. Overall it looks good but there are a few things I wanted to flag. I can't see anywhere that AppError from webhooks-journal-example/src/utils/error.ts is thrown, so it looks like it may just be dead code. The docs mention cleaning up unused subscriptions, I think it would be good to include an example of this, as well as the exponential backoff from the retry headers, it looks like currently it attempts again on the next poll. Please let me know if I misunderstood anything or if you have any questions!

Addresses PR review feedback and adds dynamic per-portal provisioning
driven by APP_LIFECYCLE events.

Review feedback:
- Wire the previously-unused AppError through a centralized errorHandler
  plus an asyncHandler wrapper; routes throw AppError for validation and
  let service/axios errors propagate (removes per-route try/catch).
- Subscription cleanup: delete a single subscription or all of a portal's
  subscriptions (routes + service fns + UI trash/Delete-All actions), plus
  an Add Subscription button and empty state so the banner is recoverable.
- Exponential backoff honoring Retry-After for journal fetches
  (src/utils/retry.ts) instead of waiting for the next poll on 429.

App-lifecycle events:
- Create app-global APP_LIFECYCLE_EVENT subscriptions (install + uninstall)
  on startup and poll from boot so they are consumed.
- Process appLifecycleEvent entries: on install, register the portal and
  dynamically create its default contact subscription; on uninstall, drop
  the portal (subscriptions are cleaned up server-side).
- Portal registry table backs the dropdown; the UI polls /api/portals and
  diffs to toast new installs and drop uninstalled portals. Lifecycle subs
  are hidden from the UI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zprobs

zprobs commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks @zaradford, I pushed fixes for those suggestions and also added some listening for app installs and uninstalls since that is a useful pattern to document.

One thing to note before we can merge, is that we will need to wait for the 2027.03-beta API release to be able to use the bulk export endpoint and some other API improvements. Once that goes live, we can update this example and merge it in.

The bulk snapshot endpoint (crm-bulk) only exists in the 2027-03-beta
date-based version, so the example now targets that version for every
journal, subscription and snapshot call, with the version in a single
constant so moving to 2027-03 GA is a one-line change.

Behavior changes the new version requires or exposed while testing:
- crm-bulk takes installPortalId and returns the status id as `id`.
- batch/{offset}/next returns 404 for an expired offset instead of an
  empty 200, so the poller now resets its cursor rather than stalling.
- First-run cursor init starts from the earliest entry; starting from
  latest skipped everything already in the journal.
- numErrors/errors are omitted when a batch has no errors, which made
  every idle poll log "Fetched 0 journal entries".
@zprobs
zprobs merged commit 67b171f into main Oct 20, 2026
1 check passed
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.

2 participants