Skip to content

[FEAT] : Webhook Redelivery Nested Payload Bug  #3128

Closed

Description

@hrshjswniii

Problem Statement

Outbound webhooks in DevTrack dispatch a structured payload of type WebhookPayload (matching the shape { event: string, timestamp: string, data: Record<string, unknown> }). When a webhook delivery fails, this complete payload structure is logged in the webhook_deliveries table under the payload column.

When a developer clicks "Retry Delivery" in the webhooks dashboard, the retry route handler /api/webhooks/custom/[id]/deliveries/[deliveryId]/retry/route.ts reads this logged payload from the database and forwards it directly as the third parameter (data) to dispatchWebhook. Because dispatchWebhook automatically wraps its data argument in a new WebhookPayload shell, the final HTTP POST request is sent with a corrupt, nested structure:

{
  "event": "goal.completed",
  "timestamp": "2026-07-04T10:00:00.000Z",
  "data": {
    "event": "goal.completed",
    "timestamp": "2026-07-03T12:00:00.000Z",
    "data": {
      // Original data is nested here
    }
  }
}

This violates the API contract and breaks integrations that parse the webhook payload structure.

tch.

Proposed Solution

Extract and forward the nested data block of the original payload during redelivery:

  1. In the retry route handler src/app/api/webhooks/custom/[id]/deliveries/[deliveryId]/retry/route.ts, parse the delivery.payload database column.
  2. Check if the parsed payload follows the standard WebhookPayload structure (i.e. contains a top-level data key).
  3. If it does, extract payloadData.data and pass that extracted dictionary to dispatchWebhook instead of the full root object:
    // Extract original inner data to prevent double-nesting on dispatch
    const originalData = (payloadData && typeof payloadData === "object" && "data" in payloadData)
      ? (payloadData.data as Record<string, unknown>)
      : payloadData;
    
    const result2 = await dispatchWebhook(id, delivery.event, originalData);
  4. This ensures that when dispatchWebhook constructs the new payload, it wraps only the original event fields.

Feature Area

API / Backend

Alternatives Considered

  • Modifying dispatchWebhook: We considered modifying the core helper in src/lib/webhooks.ts to detect if the incoming data is already wrapped in a WebhookPayload. However, dispatchWebhook is called from various live events (goals, streaks, achievements) that always supply raw dictionaries. Adding check-logic there introduces unnecessary complexity to a critical path. Normalizing the input at the retry route boundary is cleaner and safer.

Acceptance Criteria

  • Outbound webhook retries dispatch a payload structure that matches the original dispatch payload format (no nested event/timestamp keys inside the data object).
  • Unit tests exist covering retry payload formatting, ensuring that the payload remains properly un-nested during a simulated retry dispatch.

Additional Context

No response

Activity

  1. hrshjswniii commented on Jul 4, 2026

    @hrshjswniii
    ContributorAuthor

    Hiii @Priyanshu-byte-coder , i'd like to work on this issue as part of GSSoC'26

  2. github-actions commented on Jul 4, 2026

    @github-actions

    Assigned to @hrshjswniii! You have 7 days to open a PR. No PR after 7 days = auto-unassigned so others can pick it up. Good luck!

  3. changed the title [-][FEAT] : Webhook Redelivery Nested Payload Bug (Quick Win - High Impact)[/-] [+][FEAT] : Webhook Redelivery Nested Payload Bug [/+] on Jul 4, 2026
  4. github-actions commented on Jul 12, 2026

    @github-actions

    ✅ Closed as completed!

    If DevTrack has been useful, a ⭐ star on the repo helps the project grow and reach more developers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions