Skip to content

feat: signed webhooks with retry support#652

Open
sungdark wants to merge 1 commit intorohitdash08:mainfrom
sungdark:feature/signed-webhooks
Open

feat: signed webhooks with retry support#652
sungdark wants to merge 1 commit intorohitdash08:mainfrom
sungdark:feature/signed-webhooks

Conversation

@sungdark
Copy link
Copy Markdown

Summary

Implements signed webhook delivery for key events with HMAC-SHA256 signatures and retry support, addressing issue #77.

Features

Signed Delivery

  • HMAC-SHA256 signatures via X-Webhook-Signature header
  • Replay attack protection via timestamp validation (5-minute window)
  • Secret rotation endpoint provided

Retry Support

  • Automatic retry on failure (non-2xx response or timeout)
  • Exponential backoff: 1 min → 5 min → 15 min
  • Up to 3 delivery attempts

Event Types

  • expense.created - fired when an expense is created
  • expense.updated - fired when an expense is modified
  • expense.deleted - fired when an expense is deleted
  • bill.due - fired when a bill is created (indicating upcoming due)
  • reminder.sent - fired when a reminder is sent

API Endpoints

Method Endpoint Description
GET /webhooks List subscriptions
POST /webhooks Create subscription
GET /webhooks/:id Get subscription
PATCH /webhooks/:id Update subscription
DELETE /webhooks/:id Delete subscription
POST /webhooks/:id/rotate-secret Rotate signing secret
GET /webhooks/events List supported event types

Webhook Payload Format

{
  "event": "expense.created",
  "timestamp": "2026-03-26T14:30:00+00:00",
  "data": {
    "id": 1,
    "amount": 100.00,
    "currency": "INR",
    "description": "Lunch",
    "date": "2026-03-26"
  }
}

Signature Verification

Receivers should verify:

  1. Timestamp is within 5 minutes of current time
  2. X-Webhook-Signature matches sha256=HMAC(timestamp + "." + body, secret)
# Python verification example
import hmac, hashlib, time

def verify(payload: str, timestamp: str, signature: str, secret: str) -> bool:
    ts = int(timestamp)
    if abs(time.time() - ts) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{ts}.{payload}".encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

Payment

Please send $50 via Wise/crypto to my payment details.


Closes #77

Implements signed webhook delivery for key events with HMAC-SHA256
signatures, retry support, and comprehensive event documentation.

Features:
- Signed delivery using HMAC-SHA256 (X-Webhook-Signature header)
- Replay attack protection via timestamp validation (5min window)
- Automatic retry with exponential backoff (1min, 5min, 15min)
- Webhook subscription management API (CRUD)
- Event types documented in service docstring

Event types:
- expense.created
- expense.updated
- expense.deleted
- bill.due
- reminder.sent

Signed-off-by: Claude <noreply@anthropic.com>
@sungdark sungdark requested a review from rohitdash08 as a code owner March 26, 2026 14:44
@sungdark sungdark mentioned this pull request Mar 26, 2026
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.

Webhook Event System

1 participant