Skip to content

Implement WebhooksService and restore commented-out webhook triggers #102

@phertyameen

Description

@phertyameen

Summary

The WebhooksService is referenced throughout the codebase but does not exist. Four webhook triggers in ClaimRedemptionProvider are commented out with TEMPORARY: markers - sweep.completed and sweep.failed. Without webhooks, operators integrating with the SDK have no event-driven way to know when a claim succeeds or fails, forcing them to poll the GET /accounts/:id endpoint instead. This issue implements the WebhooksService and restores all commented-out triggers, plus adds the missing triggers for account creation and expiry.

Background

What webhooks are for in this context
Bridgelet is described as infrastructure for organizations running mass payments - payroll, aid disbursements, airdrops. Those organizations need to know in real time when a recipient claims funds (to update their own records), when a sweep fails (to investigate), and when an account expires unclaimed (to trigger a retry or flag for support). Without webhooks they cannot build reliable integrations on top of the SDK.

The commented-out triggers that must be restored
Search the codebase for TEMPORARY: comments - the SDK README documents their locations. Specifically in src/modules/claims/providers/claim-redemption.provider.ts:

sweep.completed - fires when a claim is redeemed successfully, with accountId, amount, asset, destination, txHash, and sweptAt
sweep.failed - fires when a sweep attempt fails after token verification passes, with accountId, amount, asset, destination, and the error message

Additional triggers not yet implemented anywhere

account.created - fires when AccountsService.create() completes successfully
account.expired - fires when the expiry scheduler (Issue #100) marks an account as EXPIRED

What WebhooksService should do
At MVP, webhook delivery does not need to be a full retry-queue system. A reasonable MVP implementation:

Operators register webhook URLs via POST /webhooks endpoint (already listed in the SDK README's key endpoints but not implemented)
When an event fires, WebhooksService POSTs the event payload as JSON to each registered URL
Include a signature header so receivers can verify the payload came from Bridgelet - a standard approach is X-Bridgelet-Signature: sha256= using the webhook secret
If delivery fails (non-2xx response or timeout), log the failure with the event type and account ID - retry logic can be a post-MVP enhancement
Webhook subscriptions should be stored in the database with at minimum: id, url, events (array of subscribed event types), secret, createdAt

Where to build this

The src/modules/webhooks/ directory is referenced in the SDK README as the intended location and already has a placeholder in the project structure. Build the module there. It needs its own entity, service, controller, and a simple HTTP client for outbound delivery (@nestjs/axios or Node's built-in fetch are both fine).

Where to look

  • src/modules/claims/providers/claim-redemption.provider.ts- the four TEMPORARY: commented blocks to restore
  • SDK README ⚠️ TEMPORARY DEVELOPMENT WORKAROUNDS section: documents all commented-out webhook locations
  • src/modules/accounts/accounts.service.ts: where account.created trigger should be added
  • src/modules/scheduler/ (Issue Implement account expiry scheduler and INITIALIZING cleanup job #100): where account.expired trigger should be added
  • The key endpoints list in the SDK README: GET /webhooks and POST /webhooks are already documented as intended endpoints

Acceptance Criteria

  • WebhooksModule exists in src/modules/webhooks/ with entity, service, and controller
  • POST /webhooks allows registering a webhook URL with a list of subscribed event types
  • GET /webhooks lists registered webhook subscriptions
  • All four TEMPORARY: commented webhook triggers in ClaimRedemptionProvider are restored and working
  • account.created trigger is added to AccountsService
  • account.expired trigger is added to the expiry scheduler from Issue 26
  • Outbound webhook requests include an HMAC signature header
  • Failed deliveries are logged with event type, account ID, destination URL, and HTTP status
  • No TEMPORARY: comments remain in the codebase after this issue is resolved
  • Unit tests cover: successful delivery, failed delivery logged without throwing, signature header present and correct

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestnestjsBackend frameworktypescriptPrograming language

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions