<?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: Mario Semper</title>
    <description>The latest articles on DEV Community by Mario Semper (@sem_pre).</description>
    <link>https://dev.to/sem_pre</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%2F326461%2F2eef4f28-67da-4631-a47f-efaf69c1cb24.png</url>
      <title>DEV Community: Mario Semper</title>
      <link>https://dev.to/sem_pre</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sem_pre"/>
    <language>en</language>
    <item>
      <title>How AI Agents Pay for API Calls — And How to Verify They Did</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Tue, 17 Mar 2026 14:59:10 +0000</pubDate>
      <link>https://dev.to/sem_pre/verifying-x402-usdc-payments-on-base-without-a-payment-processor-1ik8</link>
      <guid>https://dev.to/sem_pre/verifying-x402-usdc-payments-on-base-without-a-payment-processor-1ik8</guid>
      <description>&lt;p&gt;&lt;strong&gt;The x402 protocol just made machine-to-machine payments real. Here's what the verification layer looks like.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PayWatcher · March 2026 · 4 min read&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;HTTP has had a status code sitting unused for 30 years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;402: Payment Required.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It was reserved for a future where machines could pay for things directly. That future is here. Stripe and Coinbase are both building around x402 — a protocol that revives 402 to enable AI agents to pay for API calls in USDC, automatically, without a human in the loop.&lt;/p&gt;

&lt;p&gt;An AI agent requests a resource. The server responds with 402. The agent pays in USDC. Access is granted. No checkout page. No credit card. No human approval.&lt;/p&gt;

&lt;p&gt;This is what machine-to-machine payments look like.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Nobody Is Talking About
&lt;/h2&gt;

&lt;p&gt;Everyone is focused on the rails. Stripe built the payment layer. Coinbase built the protocol. A16z, Galaxy, Outlier Ventures are all writing theses about agentic payments.&lt;/p&gt;

&lt;p&gt;But there's a step everyone is glossing over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does the server know the payment actually arrived?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An agent sends 0.01 USDC to your wallet on Base. Your server needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect the transfer happened&lt;/li&gt;
&lt;li&gt;Match it to the specific request&lt;/li&gt;
&lt;li&gt;Confirm it's not a reorg&lt;/li&gt;
&lt;li&gt;Wait for enough block confirmations&lt;/li&gt;
&lt;li&gt;Grant access&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you build this yourself, you're looking at subscribing to USDC transfer events via ethers.js, handling edge cases like chain reorgs, managing confirmation thresholds, building retry logic for missed events. Two weeks of work, minimum — and that's if you've done it before.&lt;/p&gt;

&lt;p&gt;If you use a payment processor, you're paying 0.5–1% per transaction. On $0.01 micropayments, that's a fee larger than the payment itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The verification layer for x402 doesn't exist yet. That's the gap.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What x402 Assumes You Already Have
&lt;/h2&gt;

&lt;p&gt;The x402 protocol defines the payment flow beautifully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Agent → GET /api/resource
Server → 402 Payment Required
        { "accepts": [{ "scheme": "exact", "network": "base", 
          "maxAmountRequired": "1000", "asset": "USDC",
          "payTo": "0xYourWallet" }] }
Agent → Pays USDC on Base
Agent → GET /api/resource (with payment proof)
Server → 200 OK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean. Elegant. Stateless.&lt;/p&gt;

&lt;p&gt;But that last step — "Server verifies payment proof" — assumes your server can confirm the transfer actually happened on-chain. In practice, that means you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A blockchain listener watching your wallet&lt;/li&gt;
&lt;li&gt;Amount matching logic (how do you distinguish two 0.01 USDC payments in the same block?)&lt;/li&gt;
&lt;li&gt;Confirmation waiting (Base finalizes fast, but you still need to handle reorgs)&lt;/li&gt;
&lt;li&gt;A webhook or callback system to notify your application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's infrastructure. Non-trivial infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Verification Layer
&lt;/h2&gt;

&lt;p&gt;This is exactly what PayWatcher is built for.&lt;/p&gt;

&lt;p&gt;You create a Payment Intent before the agent pays. PayWatcher gives back a &lt;code&gt;unique_amount&lt;/code&gt; — a slightly adjusted figure (e.g., $0.0103 instead of $0.01) that makes the payment uniquely identifiable on-chain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST https://api.paywatcher.dev/v1/payments
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"amount"&lt;/span&gt;: &lt;span class="s2"&gt;"0.01"&lt;/span&gt;,
  &lt;span class="s2"&gt;"token"&lt;/span&gt;: &lt;span class="s2"&gt;"USDC"&lt;/span&gt;,
  &lt;span class="s2"&gt;"network"&lt;/span&gt;: &lt;span class="s2"&gt;"base"&lt;/span&gt;,
  &lt;span class="s2"&gt;"recipient"&lt;/span&gt;: &lt;span class="s2"&gt;"0xYourWallet"&lt;/span&gt;,
  &lt;span class="s2"&gt;"webhook_url"&lt;/span&gt;: &lt;span class="s2"&gt;"https://yourapi.com/webhook/payment"&lt;/span&gt;,
  &lt;span class="s2"&gt;"reference"&lt;/span&gt;: &lt;span class="s2"&gt;"agent-request-abc123"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pay_xyz789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"watching"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"unique_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0103"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&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-03-16T15:00:00Z"&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;You give the agent &lt;code&gt;unique_amount&lt;/code&gt; as the amount to pay. When the transfer hits Base, PayWatcher fires a webhook:&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;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payment.confirmed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"payment_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pay_xyz789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"agent-request-abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0103"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USDC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tx_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xabc...def"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confirmed_at"&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-03-16T14:47:22Z"&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;Your server grants access. Done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;$0.05 flat fee. No percentage. No custody. No checkout.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full x402 + PayWatcher Flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent → GET /api/resource
Server → 402 + PayWatcher unique_amount
Agent → Sends USDC to your wallet
PayWatcher → Detects transfer on Base
PayWatcher → Fires webhook to your server
Server → Grants access
Agent → GET /api/resource (authorized)
Server → 200 OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your server never touches the funds. PayWatcher never touches the funds. The agent pays directly to your wallet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Flat Fee Matters for Micropayments
&lt;/h2&gt;

&lt;p&gt;Percentage fees break at micropayment scale.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Payment Size&lt;/th&gt;
&lt;th&gt;Coinbase Commerce (1%)&lt;/th&gt;
&lt;th&gt;PayWatcher ($0.05 flat)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$0.01&lt;/td&gt;
&lt;td&gt;$0.0001 ✓ (but min fees apply)&lt;/td&gt;
&lt;td&gt;$0.05 ✗ too expensive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;td&gt;$0.001&lt;/td&gt;
&lt;td&gt;$0.05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;td&gt;$0.01&lt;/td&gt;
&lt;td&gt;$0.05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$10.00&lt;/td&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;td&gt;$0.05 ✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$100.00&lt;/td&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;td&gt;$0.05 ✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$10,000.00&lt;/td&gt;
&lt;td&gt;$100.00&lt;/td&gt;
&lt;td&gt;$0.05 ✓✓✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For high-value payments, flat fee wins decisively. For true micropayments under $0.05, neither model works well — but that's a Base gas cost problem, not a PayWatcher problem.&lt;/p&gt;

&lt;p&gt;The sweet spot for PayWatcher is &lt;strong&gt;$1–$10,000+ payments&lt;/strong&gt; where you want automatic verification without giving up 0.5–1%.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who This Is For
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Build with PayWatcher if you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accept USDC payments on Base and want automatic confirmation&lt;/li&gt;
&lt;li&gt;Are building x402-compatible APIs that AI agents will call&lt;/li&gt;
&lt;li&gt;Don't want to give up 0.5–1% per transaction&lt;/li&gt;
&lt;li&gt;Want webhook notifications instead of polling Basescan&lt;/li&gt;
&lt;li&gt;Need non-custodial verification (regulatory, preference, or principle)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Don't use PayWatcher if you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need fiat on/off ramps&lt;/li&gt;
&lt;li&gt;Need a hosted checkout page&lt;/li&gt;
&lt;li&gt;Need multi-currency support&lt;/li&gt;
&lt;li&gt;Are transacting under $0.05 per payment (the fee exceeds the payment)&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;PayWatcher is live on Base. Free tier: 50 verifications/month — enough to build and test your x402 integration.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://paywatcher.dev" rel="noopener noreferrer"&gt;Get API Key&lt;/a&gt; → &lt;a href="https://docs.paywatcher.dev" rel="noopener noreferrer"&gt;Read the Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built by &lt;a href="https://masem.at" rel="noopener noreferrer"&gt;masemIT&lt;/a&gt; — we also build &lt;a href="https://chainsights.one" rel="noopener noreferrer"&gt;ChainSights&lt;/a&gt;, identity-first analytics for DAOs.&lt;/p&gt;

&lt;p&gt;Questions? Reach out on &lt;a href="https://warpcast.com/paywatcher" rel="noopener noreferrer"&gt;Farcaster&lt;/a&gt; or &lt;a href="https://x.com/X_Sem_pre" rel="noopener noreferrer"&gt;X&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>api</category>
      <category>javascript</category>
      <category>web3</category>
    </item>
    <item>
      <title>Building a Context API for AI Agents: Goodbye GitHub Raw URLs</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Sun, 22 Feb 2026 19:45:14 +0000</pubDate>
      <link>https://dev.to/sem_pre/building-a-context-api-for-ai-agents-goodbye-github-raw-urls-2ma1</link>
      <guid>https://dev.to/sem_pre/building-a-context-api-for-ai-agents-goodbye-github-raw-urls-2ma1</guid>
      <description>&lt;p&gt;&lt;em&gt;How I solved Claude's "amnesia problem" with a simple microservice&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Every conversation with an AI assistant starts the same way: from scratch.&lt;/p&gt;

&lt;p&gt;If you're using Claude, ChatGPT, or any LLM for serious work – coding, product development, writing – you know the pain. You have project briefs, architecture docs, PRDs, and user stories. And every single chat, you're either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy-pasting walls of text&lt;/li&gt;
&lt;li&gt;Re-explaining context you've explained 100 times&lt;/li&gt;
&lt;li&gt;Watching the AI confidently hallucinate details it should know&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I got tired of this. So I built a Context API that gives Claude persistent, structured access to my project documentation. Here's how – and why you might want something similar.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: GitHub Raw URLs Suck for AI Context
&lt;/h2&gt;

&lt;p&gt;My first attempt was simple: store all project docs in a GitHub repo and have Claude fetch them via raw URLs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://raw.githubusercontent.com/myorg/context/master/projects/chainsights/prd.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Elegant, right? Version controlled, easy to update, free hosting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Except it doesn't work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GitHub's CDN caches raw files aggressively. Even with &lt;code&gt;?nocache=1&lt;/code&gt; appended, I'd often get stale content. Claude would reference outdated requirements. Bugs would slip through because the AI was working with last week's architecture doc.&lt;/p&gt;

&lt;p&gt;Other problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No structured queries&lt;/strong&gt; – you get raw markdown, nothing more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No delta updates&lt;/strong&gt; – fetch everything or nothing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No write-back&lt;/strong&gt; – Claude can't persist insights or tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-repo sync hell&lt;/strong&gt; – docs live in project repos, but Claude needs them centralized&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I needed something better.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: A Dedicated Context Service
&lt;/h2&gt;

&lt;p&gt;I built a simple API service that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Receives docs from GitHub Actions&lt;/strong&gt; – when I push to &lt;code&gt;_bmad-output/&lt;/code&gt; in any project, a webhook syncs to the API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serves structured JSON to Claude&lt;/strong&gt; – not raw markdown, but typed documents with metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supports delta queries&lt;/strong&gt; – "give me only what changed since yesterday"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeps version history&lt;/strong&gt; – every update is preserved&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detects orphans&lt;/strong&gt; – renamed or deleted docs don't become zombies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┐     GitHub Action      ┌─────────────────┐
│  Project Repo   │ ───────────────────▶  │  Context API    │
│  (chainsights)  │   POST /sync           │  (api.masem.at) │
└─────────────────┘                        └────────┬────────┘
                                                    │
┌─────────────────┐                                 │
│  Project Repo   │ ───────────────────────────────▶│
│  (tellingcube)  │                                 │
└─────────────────┘                                 │
                                                    ▼
                                           ┌─────────────────┐
                                           │    Claude       │
                                           │  GET /context   │
                                           └─────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Data Model
&lt;/h2&gt;

&lt;p&gt;Three tables. That's it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Projects
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ctx_projects&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;-- "chainsights"&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;-- "ChainSights"  &lt;/span&gt;
  &lt;span class="n"&gt;github_repo&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c1"&gt;-- "myorg/chainsights"&lt;/span&gt;
  &lt;span class="n"&gt;is_active&lt;/span&gt; &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Documents
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ctx_documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;project_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;ctx_projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;doc_type&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;-- "brief", "prd", "epic", "story"&lt;/span&gt;
  &lt;span class="n"&gt;doc_key&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;-- "prd", "epic-auth-flow"&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;file_sha&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;              &lt;span class="c1"&gt;-- Git SHA for change detection&lt;/span&gt;
  &lt;span class="k"&gt;version&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Document History
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ctx_document_history&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;document_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;ctx_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;file_sha&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;version&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;file_sha&lt;/code&gt; is key – when the GitHub Action syncs, we compare SHAs. Same SHA? Skip the update. Different SHA? Archive the old version, update the current.&lt;/p&gt;

&lt;p&gt;Idempotent syncs. No duplicate writes. Clean history.&lt;/p&gt;




&lt;h2&gt;
  
  
  The API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reading (for Claude)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get all docs for a project&lt;/span&gt;
GET /v1/context/chainsights

&lt;span class="c"&gt;# Get only briefs and PRDs&lt;/span&gt;
GET /v1/context/chainsights?types&lt;span class="o"&gt;=&lt;/span&gt;brief,prd

&lt;span class="c"&gt;# Get changes since a timestamp&lt;/span&gt;
GET /v1/context/chainsights?since&lt;span class="o"&gt;=&lt;/span&gt;2024-02-20T00:00:00Z

&lt;span class="c"&gt;# Get a specific document&lt;/span&gt;
GET /v1/context/chainsights/epic/auth-flow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&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;"project"&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="nl"&gt;"slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chainsights"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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;"ChainSights"&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="nl"&gt;"documents"&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;span class="nl"&gt;"doc_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"doc_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Product Requirements Document"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"# ChainSights PRD&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;## Overview..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-02-21T09:15:00Z"&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;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-02-21T09:15:00Z"&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;h3&gt;
  
  
  Writing (from GitHub Actions)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST /v1/context/chainsights/sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"documents"&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;span class="nl"&gt;"doc_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"doc_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Product Requirements Document"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"file_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"_bmad-output/prd.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"file_sha"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a1b2c3d4..."&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;span class="nl"&gt;"known_paths"&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="s2"&gt;"_bmad-output/prd.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"_bmad-output/epics/auth-flow.md"&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;Response:&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;"synced"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updated"&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;"unchanged"&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;"orphaned"&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;span class="nl"&gt;"doc_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"epic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"doc_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"old-feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"file_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"_bmad-output/epics/old-feature.md"&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;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;The &lt;code&gt;known_paths&lt;/code&gt; array is clever – it tells the API "these are all the docs that currently exist." Anything in the database but not in this list? That's an orphan. Maybe it was renamed, maybe deleted. The API flags it so I can clean up.&lt;/p&gt;




&lt;h2&gt;
  
  
  The GitHub Action
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sync BMAD Output to Context API&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_bmad-output/**'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sync to Context Service&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MMS_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MMS_CONTEXT_API_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Collect all docs, build JSON payload&lt;/span&gt;
          &lt;span class="s"&gt;# POST to api.masem.at/v1/context/chainsights/sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuration per project (&lt;code&gt;.github/mms-context.yml&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chainsights&lt;/span&gt;
&lt;span class="na"&gt;documents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;_bmad-output/brief.md&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;brief&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;brief&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;_bmad-output/prd.md&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prd&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prd&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;_bmad-output/epics/*.md&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;epic&lt;/span&gt;
    &lt;span class="c1"&gt;# key derived from filename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every push to &lt;code&gt;_bmad-output/&lt;/code&gt; triggers a sync. Docs flow from my repo to the API automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Integrating with Claude
&lt;/h2&gt;

&lt;p&gt;Here's the magic part. In Claude's system prompt (or custom instructions), I add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You have access to the MMS Context Service for project documentation.

API-Key: mms_context_claude_xxxxx
Base-URL: https://api.masem.at

Endpoints:
- GET /v1/context/{project}?types=brief,prd&amp;amp;format=summary
- GET /v1/context/{project}/{doc_type}/{doc_key}

When the user mentions "Context: {project}", fetch the relevant documents.
Load summaries first, then fetch details as needed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when I start a chat with "Context: chainsights", Claude:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetches the project summary (titles + first 500 chars)&lt;/li&gt;
&lt;li&gt;Understands what docs are available&lt;/li&gt;
&lt;li&gt;Loads specific docs when needed for the task&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No more copy-pasting. No more "as I mentioned in our last conversation." Claude just &lt;em&gt;knows&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Caching Done Right
&lt;/h2&gt;

&lt;p&gt;Since we're solving a caching problem, we better cache properly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-side (Redis):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context:chainsights:full           → 5 min TTL
context:chainsights:prd            → 5 min TTL
context:chainsights:epic:auth-flow → 5 min TTL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a sync happens, we invalidate only the affected keys. Granular, not nuclear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client-side (ETag):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Response: ETag: "sha256-of-content"
Request:  If-None-Match: "sha256-of-content"
Response: 304 Not Modified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude's requests include the ETag from the last fetch. If nothing changed, we return 304 – fast and bandwidth-friendly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delta updates:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /v1/context/chainsights?since=2024-02-20T00:00:00Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude can ask "what changed since my last sync?" and only get the deltas. Perfect for long-running conversations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;After two weeks of using this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero stale context issues&lt;/strong&gt; – Claude always has the latest docs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster conversations&lt;/strong&gt; – no preamble, no re-explaining&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better AI output&lt;/strong&gt; – consistent terminology, accurate references&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner repos&lt;/strong&gt; – no more &lt;code&gt;masemit-context&lt;/code&gt; sync repo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The service handles 7 projects, ~50 documents, and costs me exactly $0/month extra (it runs on existing infrastructure).&lt;/p&gt;




&lt;h2&gt;
  
  
  Should You Build This?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Yes, if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You use AI assistants for ongoing project work (not just one-off questions)&lt;/li&gt;
&lt;li&gt;You have structured documentation (PRDs, specs, architecture docs)&lt;/li&gt;
&lt;li&gt;You're frustrated by context limits and repetition&lt;/li&gt;
&lt;li&gt;You already have some API infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Maybe not, if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're doing ad-hoc AI tasks with no recurring context&lt;/li&gt;
&lt;li&gt;Your docs change rarely (just upload to Claude Projects)&lt;/li&gt;
&lt;li&gt;You don't want to maintain another service&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm considering open-sourcing this as a standalone service. If there's interest, I'll clean up the code and publish it.&lt;/p&gt;

&lt;p&gt;Features I'm still thinking about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full-text search&lt;/strong&gt; across all documents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task write-back&lt;/strong&gt; – Claude can create tasks that sync back to GitHub Issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhooks&lt;/strong&gt; – notify Slack when syncs fail&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenant&lt;/strong&gt; – let other teams use it&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;GitHub raw URLs have caching problems that break AI context&lt;/li&gt;
&lt;li&gt;A simple Context API solves this: sync via GitHub Actions, serve via REST&lt;/li&gt;
&lt;li&gt;SHA-based idempotency prevents duplicate writes&lt;/li&gt;
&lt;li&gt;Delta queries and ETags keep things fast&lt;/li&gt;
&lt;li&gt;Claude gets reliable, structured, up-to-date project context&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're building something similar or want to discuss the approach, drop a comment or find me on &lt;a href="https://www.linkedin.com/in/mariosemper/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://farcaster.xyz/chainsightsone.eth" rel="noopener noreferrer"&gt;Farcaster&lt;/a&gt; or &lt;a href="https://x.com/X_Sem_pre" rel="noopener noreferrer"&gt;X&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Mario Semper is the founder of &lt;a href="https://masem.at" rel="noopener noreferrer"&gt;masemIT&lt;/a&gt;, building developer tools and Web3 analytics. He's currently shipping &lt;a href="https://chainsights.one" rel="noopener noreferrer"&gt;ChainSights&lt;/a&gt; (DAO governance analytics), &lt;a href="https://tellingcube.com" rel="noopener noreferrer"&gt;tellingCube&lt;/a&gt; (synthetic business data), &lt;a href="https://paywatcher.dev" rel="noopener noreferrer"&gt;PayWatcher&lt;/a&gt; (stablecoin payment verification), and occasionally yelling at &lt;a href="https://github.com/masem-at/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;'s CDN.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Verify USDC Payments on Base Without a Payment Processor</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Sun, 22 Feb 2026 07:10:48 +0000</pubDate>
      <link>https://dev.to/sem_pre/how-to-verify-usdc-payments-on-base-without-a-payment-processor-190l</link>
      <guid>https://dev.to/sem_pre/how-to-verify-usdc-payments-on-base-without-a-payment-processor-190l</guid>
      <description>&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;You want to accept a $10,000 USDC payment. You have two options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A:&lt;/strong&gt; Integrate a payment processor like Coinbase Commerce. Set up an account, embed their checkout widget, handle their SDK. Pay $100 in fees (1%).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B:&lt;/strong&gt; Build your own blockchain listener. Learn ethers.js, subscribe to USDC transfer events, handle reorgs, confirmations, edge cases. Two weeks of work, minimum.&lt;/p&gt;

&lt;p&gt;There's no middle ground. No service that just tells you: &lt;em&gt;"Yes, this specific payment arrived."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Until now.&lt;/p&gt;




&lt;h2&gt;
  
  
  What If Verification Was a Simple API Call?
&lt;/h2&gt;

&lt;p&gt;PayWatcher is a verification layer for stablecoin payments on Base. It doesn't process payments. It doesn't touch your funds. It doesn't require a checkout flow.&lt;/p&gt;

&lt;p&gt;You tell it what payment you expect. It watches the blockchain. When the payment arrives, you get a webhook.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the cost comparison for verifying a $10,000 USDC transfer:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Fee&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Coinbase Commerce&lt;/td&gt;
&lt;td&gt;$100.00&lt;/td&gt;
&lt;td&gt;1% of transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NOWPayments&lt;/td&gt;
&lt;td&gt;$50.00&lt;/td&gt;
&lt;td&gt;0.5% of transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PayWatcher&lt;/td&gt;
&lt;td&gt;$0.05&lt;/td&gt;
&lt;td&gt;Flat fee, always&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The fee is the same whether you're verifying $1 or $1,000,000.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Works (3 Steps)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Payment Intent
&lt;/h3&gt;

&lt;p&gt;Tell PayWatcher what payment you're expecting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.paywatcher.dev/v1/payments &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "amount": "10000.00",
    "token": "USDC",
    "network": "base",
    "recipient": "0xYourWalletAddress",
    "webhook_url": "https://yourapp.com/webhook/payment",
    "reference": "invoice-2026-001"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pay_abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"watching"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"unique_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10000.03"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&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-02-23T12:00:00Z"&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;Notice the &lt;code&gt;unique_amount&lt;/code&gt;? PayWatcher adds a few cents to make your payment uniquely identifiable on-chain. Your customer sends $10,000.03 instead of $10,000.00 — and that tiny difference is how we match the exact transfer to your intent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Your Customer Sends USDC
&lt;/h3&gt;

&lt;p&gt;Your customer sends USDC directly to your wallet. No checkout page, no intermediary, no redirect. Just a standard USDC transfer on Base.&lt;/p&gt;

&lt;p&gt;You keep 100% of the payment. PayWatcher never has custody.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Get Notified
&lt;/h3&gt;

&lt;p&gt;When the transfer is confirmed on Base, PayWatcher fires a webhook:&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;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payment.confirmed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"payment_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pay_abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"invoice-2026-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10000.03"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USDC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tx_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xabc...def"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confirmed_at"&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-02-22T10:30:00Z"&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;Your backend processes the webhook, marks the invoice as paid, and you're done. No polling, no manual checking on Basescan.&lt;/p&gt;




&lt;h2&gt;
  
  
  JavaScript Example
&lt;/h2&gt;

&lt;p&gt;Here's a minimal Node.js integration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a payment intent&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.paywatcher.dev/v1/payments&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYWATCHER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;500.00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USDC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0xYourWallet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourapp.com/api/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`order-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Show payment.unique_amount to your customer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the webhook handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express webhook endpoint&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tx_hash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payment.confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Mark order as paid&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reference&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tx_hash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  When to Use PayWatcher (and When Not To)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use PayWatcher when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You accept USDC payments and want automatic confirmation&lt;/li&gt;
&lt;li&gt;You don't want to give up 0.5–1% per transaction&lt;/li&gt;
&lt;li&gt;You need a non-custodial solution (regulatory, preference, or principle)&lt;/li&gt;
&lt;li&gt;You want webhook notifications instead of polling Basescan&lt;/li&gt;
&lt;li&gt;You're building on Base&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Don't use PayWatcher when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need fiat on/off ramps (use Coinbase Commerce or Stripe)&lt;/li&gt;
&lt;li&gt;You need multi-currency checkout flows&lt;/li&gt;
&lt;li&gt;You want a hosted payment page (PayWatcher is API-only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PayWatcher is verification, not processing. We don't hold, transfer, or have custody of funds.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Unique About This Approach?
&lt;/h2&gt;

&lt;p&gt;Most crypto payment solutions are trying to be the &lt;em&gt;next Stripe for crypto&lt;/em&gt; — full checkout flows, currency conversion, custody, settlement. That's valuable for some use cases.&lt;/p&gt;

&lt;p&gt;But if you're already comfortable with USDC on Base, you don't need all that. You need someone to watch the blockchain and tell you when money arrived. That's a fundamentally different service, and it should be priced differently.&lt;/p&gt;

&lt;p&gt;A flat $0.05 per verification instead of a percentage of the transaction value.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your App                    PayWatcher                  Base Network
   │                            │                            │
   ├── POST /v1/payments ──────►│                            │
   │   (create intent)          │                            │
   │◄── { unique_amount } ──────┤                            │
   │                            │                            │
   │   Show amount to customer  │                            │
   │                            │                            │
   │                            │◄── Monitor USDC transfers ─┤
   │                            │    (event listener)        │
   │                            │                            │
   │                            │── Match transfer to intent │
   │                            │── Wait for confirmations   │
   │                            │                            │
   │◄── Webhook: confirmed ─────┤                            │
   │                            │                            │
   └── Process payment          │                            │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PayWatcher sits between your application and the blockchain. It handles the complexity of monitoring transfers, matching amounts, waiting for confirmations, and handling edge cases like reorgs — so you don't have to.&lt;/p&gt;




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

&lt;p&gt;PayWatcher is live on Base. We're onboarding early testers now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free tier:&lt;/strong&gt; 50 verifications/month — enough to test and validate your integration.&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;&lt;a href="https://paywatcher.dev/#request-access" rel="noopener noreferrer"&gt;Request API Access&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
→ &lt;strong&gt;&lt;a href="https://paywatcher.dev/docs" rel="noopener noreferrer"&gt;Read the Docs&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Built by &lt;a href="https://masem.at" rel="noopener noreferrer"&gt;masemIT&lt;/a&gt; — we also build &lt;a href="https://chainsights.one" rel="noopener noreferrer"&gt;ChainSights&lt;/a&gt;, identity-first analytics for DAOs.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions? Reach out on &lt;a href="https://farcaster.xyz/paywatcher" rel="noopener noreferrer"&gt;Farcaster&lt;/a&gt; or &lt;a href="https://x.com/PayWatcher_dev" rel="noopener noreferrer"&gt;X&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>api</category>
      <category>blockchain</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>PayWatcher is Live</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Sat, 21 Feb 2026 19:18:24 +0000</pubDate>
      <link>https://dev.to/sem_pre/paywatcher-is-live-50cp</link>
      <guid>https://dev.to/sem_pre/paywatcher-is-live-50cp</guid>
      <description>&lt;p&gt;We just shipped PayWatcher — payment verification for USDC on Base.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;The problem:&lt;/u&gt; &lt;br&gt;
verify a $10k stablecoin payment and you either pay $100 to a processor or build your own blockchain listener from scratch.&lt;/p&gt;

&lt;p&gt;PayWatcher is the missing middle: create a payment intent, we watch Base for the transfer, webhook fires when confirmed. $0.05 flat. Non-custodial. You keep 100%.&lt;/p&gt;

&lt;p&gt;Live now → &lt;a href="https://paywatcher.dev?utm_source=devto" rel="noopener noreferrer"&gt;paywatcher.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking for devs building on Base who accept USDC. &lt;br&gt;
&lt;strong&gt;Free tier available.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>cryptocurrency</category>
    </item>
    <item>
      <title>How I Built a Donation Website in 8 Hours (And Why)</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Sun, 18 Jan 2026 00:33:21 +0000</pubDate>
      <link>https://dev.to/sem_pre/how-i-built-a-donation-website-in-8-hours-and-why-1ka9</link>
      <guid>https://dev.to/sem_pre/how-i-built-a-donation-website-in-8-hours-and-why-1ka9</guid>
      <description>&lt;h2&gt;
  
  
  The Mission
&lt;/h2&gt;

&lt;p&gt;Yesterday, I built &lt;a href="https://hoki.help" rel="noopener noreferrer"&gt;hoki.help&lt;/a&gt; – a donation platform for HoKi NÖ, a children's hospice organization in Lower Austria.&lt;/p&gt;

&lt;p&gt;HoKi supports families with seriously ill children. From diagnosis through illness to bereavement support. Completely free of charge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 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;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;Next.js 14 (App Router)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;td&gt;Tailwind CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payments&lt;/td&gt;
&lt;td&gt;Stripe Checkout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;Neon (Serverless Postgres)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosting&lt;/td&gt;
&lt;td&gt;Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain&lt;/td&gt;
&lt;td&gt;hoki.help&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;In approximately 8 hours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Responsive landing page (mobile-first)&lt;/li&gt;
&lt;li&gt;✅ Donation widget (€10/25/50/100 + custom amounts)&lt;/li&gt;
&lt;li&gt;✅ One-time and monthly recurring donations&lt;/li&gt;
&lt;li&gt;✅ Anonymous donation option&lt;/li&gt;
&lt;li&gt;✅ Stripe Checkout integration&lt;/li&gt;
&lt;li&gt;✅ Webhook handling for donation tracking&lt;/li&gt;
&lt;li&gt;✅ Donation barometer (shows progress after €500)&lt;/li&gt;
&lt;li&gt;✅ Legal pages (Impressum, Privacy Policy - GDPR compliant)&lt;/li&gt;
&lt;li&gt;✅ FAQ section with Schema.org structured data&lt;/li&gt;
&lt;li&gt;✅ SEO optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Key Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Stripe Checkout (Not Custom Forms)
&lt;/h3&gt;

&lt;p&gt;Why handle PCI compliance ourselves? Stripe Checkout is secure, trusted, and supports Apple Pay / Google Pay out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Neon over Vercel KV
&lt;/h3&gt;

&lt;p&gt;We only need to store aggregate donation stats (total amount, count). Neon's serverless Postgres in Frankfurt keeps data in the EU and I already use it for other projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. No User Accounts
&lt;/h3&gt;

&lt;p&gt;Donations should be frictionless. No registration, no passwords. Just donate and go.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Anonymous by Design
&lt;/h3&gt;

&lt;p&gt;Donors can choose to remain anonymous. Their name won't be stored or shared with the charity – only Stripe has it for payment processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://hoki.help" rel="noopener noreferrer"&gt;hoki.help&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;100% of donations go to HoKi NÖ. I cover all hosting costs through my company.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open for Questions
&lt;/h2&gt;

&lt;p&gt;Have questions about the implementation? Drop a comment below!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with ❤️ for the children and families supported by HoKi NÖ.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>stripe</category>
      <category>charity</category>
    </item>
    <item>
      <title>🛠️ Devs, want automatic README generation + usage diagrams in every repo?</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Sat, 05 Jul 2025 17:00:24 +0000</pubDate>
      <link>https://dev.to/sem_pre/devs-want-automatic-readme-generation-usage-diagrams-in-every-repo-57mo</link>
      <guid>https://dev.to/sem_pre/devs-want-automatic-readme-generation-usage-diagrams-in-every-repo-57mo</guid>
      <description>&lt;p&gt;Try GitHub Tools — a new suite of reusable GitHub workflows from masem.at for streamlining your docs &amp;amp; release automation. Zero setup. Just plug and ship.&lt;/p&gt;

&lt;p&gt;⚡️ README.md from your code&lt;br&gt;
📊 SVG/ASCII diagrams auto-generated&lt;br&gt;
🤖 Social post drafts via OpenAI&lt;/p&gt;

&lt;p&gt;Experimental &amp;amp; open for all!&lt;br&gt;
👉 &lt;a href="https://masem.at/projects/github-tools" rel="noopener noreferrer"&gt;https://masem.at/projects/github-tools&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>github</category>
    </item>
    <item>
      <title>Announcing pyseoa-ts v0.2.0 – More Power, More Control</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Mon, 23 Jun 2025 20:35:48 +0000</pubDate>
      <link>https://dev.to/sem_pre/announcing-pyseoa-ts-v020-more-power-more-control-49ji</link>
      <guid>https://dev.to/sem_pre/announcing-pyseoa-ts-v020-more-power-more-control-49ji</guid>
      <description>&lt;p&gt;We're excited to release &lt;strong&gt;pyseoa-ts v0.2.0&lt;/strong&gt;, a TypeScript-based SEO audit library designed for modern web apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 What’s New in v0.2.0
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Progress Type Enhancements&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Progress callbacks now include &lt;code&gt;type: "crawl" | "analyze"&lt;/code&gt; for more detailed reporting in the UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Request Delay / Rate Limiting&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can now throttle requests when crawling large sites.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Custom User-Agent Support&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Specify your own User-Agent string for SEO testing and bot behavior emulation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DX Improvements&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
All key result types like.... &lt;a href="https://www.masem.at/blog/announcing-pyseoa-ts-v020-more-power-more-control" rel="noopener noreferrer"&gt;Read more&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
    <item>
      <title>I just published pyseoa-ts — a modern, type-safe SEO analyzer for HTML in React/Vite/TS projects 🚀</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Sun, 22 Jun 2025 14:50:54 +0000</pubDate>
      <link>https://dev.to/sem_pre/i-just-published-pyseoa-ts-a-modern-type-safe-seo-analyzer-for-html-in-reactvitets-projects-3cid</link>
      <guid>https://dev.to/sem_pre/i-just-published-pyseoa-ts-a-modern-type-safe-seo-analyzer-for-html-in-reactvitets-projects-3cid</guid>
      <description>&lt;p&gt;Includes tag audits, keyword density, and a scoring summary – all powered by clean TypeScript and designed to work well in Vercel, serverless and SPA contexts.&lt;/p&gt;

&lt;p&gt;👉 npm install pyseoa-ts&lt;br&gt;
🔗 &lt;a href="https://www.masem.at/blog/introducing-pyseoa-ts-typescript-seo-analyzer-for-modern-web-apps" rel="noopener noreferrer"&gt;https://www.masem.at/blog/introducing-pyseoa-ts-typescript-seo-analyzer-for-modern-web-apps&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  typescript #seo #react #vite #opensource
&lt;/h1&gt;

</description>
      <category>typescript</category>
      <category>seo</category>
      <category>react</category>
      <category>vite</category>
    </item>
    <item>
      <title>gq-to-sql v0.2.0 — Grouping, Joins, and PostgreSQL Support</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Thu, 19 Jun 2025 23:12:47 +0000</pubDate>
      <link>https://dev.to/sem_pre/gq-to-sql-v020-grouping-joins-and-postgresql-support-517i</link>
      <guid>https://dev.to/sem_pre/gq-to-sql-v020-grouping-joins-and-postgresql-support-517i</guid>
      <description>&lt;h1&gt;
  
  
  🚀 gq-to-sql v0.2.0 — Now with Grouping, Joins, and PostgreSQL Support!
&lt;/h1&gt;

&lt;p&gt;I'm excited to announce the release of &lt;code&gt;gq-to-sql&lt;/code&gt; v0.2.0 — a powerful JavaScript utility that turns Microsoft Graph-style query strings into safe, parameterized SQL.&lt;/p&gt;

&lt;p&gt;Whether you're building a REST API, internal tool, or dashboard interface, this utility gives you clean SQL generation from URLs like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$select=name&amp;amp;$expand=roles($select=label)&amp;amp;$filter=roles.label eq 'admin'&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ What’s New in v0.2.0
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$expand=table($select=...)&lt;/code&gt; with JOIN support&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$apply=groupby(..., aggregate(...))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Combine &lt;code&gt;$apply + $filter&lt;/code&gt; in one query&lt;/li&gt;
&lt;li&gt;Support for SQL dialects:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mysql&lt;/code&gt; (default): &lt;code&gt;?&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;postgres&lt;/code&gt;: &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, ...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Improved alias mapping &amp;amp; schema-based JOINs&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  📦 Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const query = "$select=name&amp;amp;$expand=roles($select=label)&amp;amp;$filter=roles.label eq 'admin'";

const { sql, params } = buildSQL(query, {
  table: 'users',
  schema: {
    users: {
      alias: 'u',
      columns: ['id', 'name', 'role_id'],
      joins: {
        roles: {
          type: 'LEFT',
          on: 'u.role_id = r.id',
          alias: 'r'
        }
      }
    },
    roles: {
      alias: 'r',
      columns: ['id', 'label']
    }
  },
  placeholderStyle: 'postgres'
});

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

&lt;/div&gt;



&lt;p&gt;👉 Output:&lt;br&gt;
&lt;code&gt;SELECT u.name, r.label FROM users u LEFT JOIN roles r ON u.role_id = r.id WHERE r.label = $1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Install it:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install gq-to-sql&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/masem1899/graphql-to-sql" rel="noopener noreferrer"&gt;github.com/masem1899/graphql-to-sql&lt;/a&gt;&lt;br&gt;
npm: &lt;a href="https://npmjs.com/package/gq-to-sql" rel="noopener noreferrer"&gt;npmjs.com/package/gq-to-sql&lt;/a&gt;&lt;br&gt;
Website: &lt;a href="https://masem.at" rel="noopener noreferrer"&gt;https://masem.at&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Introducing gq-to-sql: Convert Graph-Style URL Queries into SQL</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Wed, 18 Jun 2025 10:17:30 +0000</pubDate>
      <link>https://dev.to/sem_pre/introducing-gq-to-sql-convert-graph-style-url-queries-into-sql-4h5o</link>
      <guid>https://dev.to/sem_pre/introducing-gq-to-sql-convert-graph-style-url-queries-into-sql-4h5o</guid>
      <description>&lt;p&gt;As a developer working with Graph-like APIs and SQL databases, I often wished for an easy way to parse $filter, $select, and similar query strings and convert them into SQL. So I built gq-to-sql — a lightweight, safe utility for just that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;✅ Parse Graph-style query strings: $filter, $select, $orderby, $top, $skip&lt;/p&gt;

&lt;p&gt;✅ Safe SQL output with parameters (?)&lt;/p&gt;

&lt;p&gt;✅ Nested condition support ((age gt 20 or age lt 10))&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;?$filter=age gt 20 and city eq 'Vienna'&amp;amp;$select=name,age&amp;amp;$orderby=age desc&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT name, age FROM users WHERE age &amp;gt; ? AND city = ? ORDER BY age desc&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try It Out&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;npm install gq-to-sql&lt;/p&gt;

&lt;p&gt;👉 View it on GitHub: &lt;a href="https://github.com/masem1899/graphql-to-sql" rel="noopener noreferrer"&gt;https://github.com/masem1899/graphql-to-sql&lt;/a&gt;&lt;br&gt;
👉 Website: &lt;a href="https://masem.at/s/devto" rel="noopener noreferrer"&gt;https://masem.at/s/devto&lt;/a&gt;&lt;br&gt;
👉 Blog Article: &lt;a href="https://www.masem.at/blog/introducing-gqtosql-graphstyle-url-filtering-for-sql" rel="noopener noreferrer"&gt;https://www.masem.at/blog/introducing-gqtosql-graphstyle-url-filtering-for-sql&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>microsoftgraph</category>
      <category>sql</category>
      <category>graphql</category>
    </item>
    <item>
      <title>🎛️ vercel-debug-bar: inspect feature flags + web vitals like a boss</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Fri, 13 Jun 2025 11:04:13 +0000</pubDate>
      <link>https://dev.to/sem_pre/vercel-debug-bar-inspect-feature-flags-web-vitals-like-a-boss-55kb</link>
      <guid>https://dev.to/sem_pre/vercel-debug-bar-inspect-feature-flags-web-vitals-like-a-boss-55kb</guid>
      <description>&lt;p&gt;Tired of guessing which flag is active or what INP means? vercel-debug-bar is a plug-and-play React overlay for inspecting:&lt;/p&gt;

&lt;p&gt;✅ Vercel Feature Flags&lt;/p&gt;

&lt;p&gt;📊 Core Web Vitals (real-time via web-vitals)&lt;/p&gt;

&lt;p&gt;📈 Analytics and A/B data&lt;/p&gt;

&lt;p&gt;It’s fully TailwindCSS styled, positionable, and now publishes GOOD / POOR performance zones.&lt;/p&gt;

&lt;p&gt;powered by &lt;a href="https://masem.at/s/devto" rel="noopener noreferrer"&gt;masem&lt;/a&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
npm install vercel-debug-bar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>vercel</category>
      <category>react</category>
      <category>tailwindcss</category>
      <category>performance</category>
    </item>
    <item>
      <title>🚀 New Release: pyseoa v0.2.3</title>
      <dc:creator>Mario Semper</dc:creator>
      <pubDate>Sun, 08 Jun 2025 20:32:52 +0000</pubDate>
      <link>https://dev.to/sem_pre/new-release-pyseoa-v023-1mco</link>
      <guid>https://dev.to/sem_pre/new-release-pyseoa-v023-1mco</guid>
      <description>&lt;p&gt;🚀 New Release: pyseoa v0.2.3&lt;br&gt;
A modern Python-powered SEO analysis toolkit 🐍&lt;/p&gt;

&lt;p&gt;✅ Smart internal link crawling&lt;br&gt;
✅ CLI &amp;amp; FastAPI-ready&lt;br&gt;
✅ PDF, HTML, CSV, Markdown exports&lt;br&gt;
✅ Customizable feature flags (title, meta, OpenGraph, etc.)&lt;br&gt;
✅ Threaded performance for crawl, analyze, export&lt;/p&gt;

&lt;p&gt;📦 Now on PyPI: pip install pyseoa&lt;br&gt;
🔗 Link in bio or visit: pypi.org/project/pyseoa&lt;br&gt;
🔗 &lt;a href="https://seo.masem.at/" rel="noopener noreferrer"&gt;https://seo.masem.at/&lt;/a&gt;&lt;br&gt;
🔗 &lt;a href="https://github.com/sempre76/pyseoa" rel="noopener noreferrer"&gt;https://github.com/sempre76/pyseoa&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔧 Built for devs, open source tools, and SaaS dashboards.&lt;/p&gt;

&lt;h1&gt;
  
  
  Python #SEO #OpenSource #DevTools #FastAPI #CLI #Automation #PythonTools #SEOTools #WebTools
&lt;/h1&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>seo</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
