<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zonovra</title>
    <description>The latest articles on DEV Community by Zonovra (@zonovra).</description>
    <link>https://dev.to/zonovra</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3897645%2Fce133c9a-9b77-4cc5-a06f-b67d2b581437.png</url>
      <title>DEV Community: Zonovra</title>
      <link>https://dev.to/zonovra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zonovra"/>
    <language>en</language>
    <item>
      <title>I Built an AI Receipt Scanner That Tracks Your Spending (FastAPI + Amazon Textract)</title>
      <dc:creator>Zonovra</dc:creator>
      <pubDate>Sat, 25 Apr 2026 14:56:06 +0000</pubDate>
      <link>https://dev.to/zonovra/i-built-an-ai-receipt-scanner-that-tracks-your-spending-fastapi-amazon-textract-4lmn</link>
      <guid>https://dev.to/zonovra/i-built-an-ai-receipt-scanner-that-tracks-your-spending-fastapi-amazon-textract-4lmn</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I kept stuffing receipts in my pocket and forgetting about them. At the end of the month: "Where did all my money go?"&lt;/p&gt;

&lt;p&gt;Spreadsheets are tedious. Expense apps want you to type everything manually. I wanted something simpler — snap a photo, done.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SnapReceipt&lt;/strong&gt; — take a photo of any receipt, AI reads it automatically, and tracks your spending.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Upload a receipt photo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Any receipt from any store, any country. JPG or PNG.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. AI extracts everything&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Powered by Amazon Textract's AnalyzeExpense API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"store_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sainsbury's"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;11.80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GBP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Groceries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JS 2 WHITE BAGUETTES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.50&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JW TUNA OIL 4X 125G"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.25&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JS FAIRTRD BANANA LS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.215&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.15&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Auto-categorization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The app detects the store name and categorizes automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🛒 Groceries (Tesco, Sainsbury's, Aldi, Walmart...)&lt;/li&gt;
&lt;li&gt;🍕 Restaurant (McDonald's, Starbucks, Nando's...)&lt;/li&gt;
&lt;li&gt;🚗 Transport (Shell, BP, Uber, airlines...)&lt;/li&gt;
&lt;li&gt;🛍️ Shopping (Amazon, Argos, Primark...)&lt;/li&gt;
&lt;li&gt;💊 Health (Boots, pharmacies, doctors...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Currency detection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reads £, €, $ symbols from the receipt. Also detects currency from the store name — Sainsbury's → GBP, Carrefour → EUR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Spending analytics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Monthly summaries, category breakdowns, 6-month trends — all in the dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Biggest Challenge: File Size
&lt;/h2&gt;

&lt;p&gt;API Gateway has a ~5MB payload limit. Phone photos of receipts can be 4-8MB. My first approach (direct upload) failed for larger images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Presigned S3 upload URLs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1: POST /receipts/upload-url → get presigned S3 URL
Step 2: PUT image directly to S3 (bypasses API Gateway)
Step 3: POST /receipts/process → Textract reads from S3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works for any file size up to 10MB. It's the standard pattern for serverless file uploads — and it's what I'd recommend to anyone building file upload features on Lambda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;FastAPI on Lambda&lt;/td&gt;
&lt;td&gt;$0 (free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;$0 (free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image storage&lt;/td&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;$0 (free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Receipt OCR&lt;/td&gt;
&lt;td&gt;Amazon Textract&lt;/td&gt;
&lt;td&gt;~$0.01 per scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;HTTP API&lt;/td&gt;
&lt;td&gt;$0 (free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Total cost at 1000 scans/month: &lt;strong&gt;~$10/month&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Amazon Textract Gets Right (and Wrong)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Gets right:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store names — almost always correct&lt;/li&gt;
&lt;li&gt;Line items — reads item names and prices accurately&lt;/li&gt;
&lt;li&gt;Totals — usually finds the right total&lt;/li&gt;
&lt;li&gt;Date — handles most formats (DD/MM/YYYY, MM/DD/YYYY, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Gets wrong sometimes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Picks up phone numbers or reference numbers as the "total"&lt;/li&gt;
&lt;li&gt;Crumpled or faded receipts reduce accuracy&lt;/li&gt;
&lt;li&gt;Some handwritten receipts don't work well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My fix for wrong totals:&lt;/strong&gt; When Textract returns multiple candidate totals, I pick the one closest to the sum of line items. This eliminates phone numbers and reference numbers that are far from the actual receipt total.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_pick_best_total&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate_totals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;items_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;items_sum&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate_totals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;items_sum&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;candidate_totals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Revenue Model
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free:&lt;/strong&gt; 20 scans/month (enough for casual use)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro:&lt;/strong&gt; £5/month for unlimited scans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scan counter resets monthly. When users hit the limit, they see an upgrade prompt. The free tier is generous enough to be useful but limited enough that regular users convert.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://snapreceipt.zonovra.com" rel="noopener noreferrer"&gt;https://snapreceipt.zonovra.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Works on desktop and mobile — open in your phone browser, take a photo directly, upload.&lt;/p&gt;

&lt;p&gt;This is my first product launch. I also built &lt;a href="https://hookcatcher.zonovra.com" rel="noopener noreferrer"&gt;HookCatcher&lt;/a&gt; — a webhook debugger with replay. Both running on AWS free tier with FastAPI.&lt;/p&gt;

&lt;p&gt;What features would you add to a receipt tracker? Would love feedback.&lt;/p&gt;

</description>
      <category>python</category>
      <category>aws</category>
      <category>ai</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>I Built a Webhook Debugger with Replay — No Install, No CLI (FastAPI + AWS Lambda)</title>
      <dc:creator>Zonovra</dc:creator>
      <pubDate>Sat, 25 Apr 2026 14:45:27 +0000</pubDate>
      <link>https://dev.to/zonovra/i-built-a-webhook-debugger-with-replay-no-install-no-cli-fastapi-aws-lambda-5a5d</link>
      <guid>https://dev.to/zonovra/i-built-a-webhook-debugger-with-replay-no-install-no-cli-fastapi-aws-lambda-5a5d</guid>
      <description>&lt;h2&gt;
  
  
  The Problem Every Developer Hits
&lt;/h2&gt;

&lt;p&gt;You're integrating with Stripe. You set up a webhook endpoint. A payment comes in and... your handler crashes. &lt;/p&gt;

&lt;p&gt;Now you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See what Stripe actually sent&lt;/li&gt;
&lt;li&gt;Fix your code&lt;/li&gt;
&lt;li&gt;Trigger the same webhook again to test&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But you can't make another real payment just to test. And Stripe's webhook logs only show you the last few attempts.&lt;/p&gt;

&lt;p&gt;This happens with every webhook-based API — GitHub, Shopify, Twilio, SendGrid, Slack.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;HookCatcher&lt;/strong&gt; — a webhook debugger with one-click replay.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Create an endpoint&lt;/strong&gt; → get a unique url&lt;/p&gt;

&lt;p&gt;POST /endpoints&lt;br&gt;
{"label": "Stripe Test"}&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://api.example.com/hook/XzjDcT_55wk4zV7hexj-WQ" rel="noopener noreferrer"&gt;https://api.example.com/hook/XzjDcT_55wk4zV7hexj-WQ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Point your webhook source to that URL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Paste it into Stripe → Developers → Webhooks → Add endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Every request is captured&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Method, headers, body, query params, source IP, timestamp — everything.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
json
{
  "method": "POST",
  "headers": {
    "content-type": "application/json",
    "x-stripe-signature": "sig_test_abc123"
  },
  "body": "{\"event\":\"payment.completed\",\"amount\":49.99}",
  "source_ip": "54.187.174.169",
  "received_at": "2026-04-22T20:13:43Z"
}

4. Replay to your local server

POST /requests/replay
{
  "request_id": "5ca786ed-3d5",
  "target_url": "http://localhost:8000/webhook"
}

The exact same headers, body, and content type are sent to your local server. Fix your code, replay again. No need to trigger the real event.

Why Not Just Use ngrok / webhook.site?

Feature                 ngrok   webhook.site    HookCatcher
Receive webhooks    ✅ ✅         ✅
Request history         ❌ Limited         ✅ 24h free / 30d pro
Full header inspection  ❌ ✅         ✅
One-click replay    ❌ ❌         ✅
REST API            ❌ ❌         ✅
No install required ❌(cli)    ✅         ✅

The replay feature is the key differentiator. Capture once, replay as many times as you need.

Tech Stack
FastAPI — Python async API framework

DynamoDB — stores users, endpoints, and captured requests (pay-per-request, $0 at low traffic)

AWS Lambda + API Gateway — serverless, scales to zero, $0 hosting on free tier

DynamoDB TTL — auto-deletes old requests after 30 days (free cleanup, no cron jobs)

Total Aws cost at launch: $0/month

Architecture

Webhook source (Stripe, GitHub, etc.)
    → API Gateway
    → Lambda (FastAPI)
    → DynamoDB (store request)

Developer opens dashboard
    → Views captured requests
    → Clicks "Replay"
    → Lambda sends request to developer's target url

What I Learned Building This

1 DynamoDB ttl is underrated — set a ttl attribute with a Unix timestamp and DynamoDB deletes expired items automatically. No cron jobs, no cleanup Lambda, no cost.

2 Api Gateway catches everything — using a catch-all route ($default), any http method sent to /hook/{id} gets forwarded to Lambda. One route handles get, post, put, patch, delete.

3 Replay needs header filtering — you can't replay Host, Content-Length, or X-Forwarded-For headers. They need to be stripped before sending to the target url.

Try It

  🔗 https://hookcatcher.zonovra.com

  Free: 1 endpoint, 24-hour history

  Pro: 10 endpoints, 30-day history — £10/month

It's my second product launch this month (first was SnapReceipt — an AI receipt scanner). Both built with FastAPI + aws serverless, both running on free tier.

Would love feedback from anyone who works with webhooks. What features would make this a must-have for your workflow?

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>aws</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
