<?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: Kaven C</title>
    <description>The latest articles on DEV Community by Kaven C (@linknpark).</description>
    <link>https://dev.to/linknpark</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4013694%2F7ba5288f-40e2-427d-bbe2-3fd5233c8f9d.webp</url>
      <title>DEV Community: Kaven C</title>
      <link>https://dev.to/linknpark</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/linknpark"/>
    <language>en</language>
    <item>
      <title>One support widget, seven platforms: what shipping to each marketplace actually takes</title>
      <dc:creator>Kaven C</dc:creator>
      <pubDate>Sun, 19 Jul 2026 11:35:39 +0000</pubDate>
      <link>https://dev.to/linknpark/one-support-widget-seven-platforms-what-shipping-to-each-marketplace-actually-takes-3ik8</link>
      <guid>https://dev.to/linknpark/one-support-widget-seven-platforms-what-shipping-to-each-marketplace-actually-takes-3ik8</guid>
      <description>&lt;p&gt;I build &lt;a href="https://deskcrew.io" rel="noopener noreferrer"&gt;DeskCrew&lt;/a&gt;, a support helpdesk, solo. The product is a chat widget plus an AI that answers from your own help docs. Building it was the easy part. Getting it in front of people means shipping the same widget to every platform a customer might already be on: WordPress, Shopify, npm frameworks, forums, docs sites.&lt;/p&gt;

&lt;p&gt;This week I shipped it to seven of them. Here is what each one actually took, because the marketing pages never tell you.&lt;/p&gt;

&lt;p&gt;The whole product is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://deskcrew.io/desk.js"&lt;/span&gt; &lt;span class="na"&gt;data-key=&lt;/span&gt;&lt;span class="s"&gt;"pub_YOUR_KEY"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything below is just a different wrapper around getting that tag onto a page. But every platform has its own delivery mechanism, its own review process, and its own trap.&lt;/p&gt;

&lt;h2&gt;
  
  
  npm frameworks (Astro, Docusaurus, Nuxt): the config-entry pattern
&lt;/h2&gt;

&lt;p&gt;Modern site frameworks do not want a script tag. They want a config entry. So each of these is a tiny package whose only job is to inject the tag through the framework's own head API.&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;// astro.config.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;deskcrew&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@deskcrew/astro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;integrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;deskcrew&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;widgetKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pub_...&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;p&gt;Publishing is &lt;code&gt;npm publish --access public&lt;/code&gt;. The trap was not the code. It was &lt;strong&gt;auth&lt;/strong&gt;: enabling 2FA on my npm account silently invalidated the CLI session, and &lt;code&gt;npm publish&lt;/code&gt; responded with a 404 on the scoped package, not a 401. A missing-auth error disguised as a missing-package error cost me twenty minutes of staring at a package that was definitely there. Lesson: on npm, &lt;code&gt;E404&lt;/code&gt; on your own scope usually means "you are not logged in," not "it does not exist."&lt;/p&gt;

&lt;h2&gt;
  
  
  Shopify: the zero-scopes flex
&lt;/h2&gt;

&lt;p&gt;Shopify was the one I expected to be brutal, and it was the smoothest, because of one architectural decision: &lt;strong&gt;the app requests zero API scopes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most Shopify apps ask for &lt;code&gt;read_products&lt;/code&gt;, &lt;code&gt;read_customers&lt;/code&gt;, and so on, which drops you into Shopify's protected-customer-data review: a longer, stricter process. But my widget does not need any Shopify data. It ships as a &lt;strong&gt;theme app extension&lt;/strong&gt; (an app-embed block), not the deprecated ScriptTag API, so the merchant flips one toggle in the theme editor and the widget loads. No data access, no protected-data review, fast track.&lt;/p&gt;

&lt;p&gt;The block itself is defensive by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;widget_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;blank&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-%}&lt;/span&gt;
  &amp;lt;script src="https://deskcrew.io/desk.js"
    data-key="&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;widget_key&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="nf"&gt;escape&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;" defer&amp;gt;&amp;lt;/script&amp;gt;
&lt;span class="cp"&gt;{%-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing renders without a key, and every merchant-supplied value is escaped. The one non-obvious requirement: the mandatory GDPR webhooks (&lt;code&gt;customers/data_request&lt;/code&gt;, &lt;code&gt;customers/redact&lt;/code&gt;, &lt;code&gt;shop/redact&lt;/code&gt;) plus &lt;code&gt;app/uninstalled&lt;/code&gt;, all HMAC-verified with a timing-safe compare, returning 401 on a bad signature. Reviewers test these first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discourse: no store at all
&lt;/h2&gt;

&lt;p&gt;Discourse has no marketplace and no review queue. A "theme component" is just a public git repo with an &lt;code&gt;about.json&lt;/code&gt; and a &lt;code&gt;common/head_tag.html&lt;/code&gt;. Push it to GitHub, and any forum admin installs it with &lt;strong&gt;Admin → Customize → Themes → Install → From a git repository&lt;/strong&gt;. That is the entire distribution story. The moment I pushed, it was installable worldwide.&lt;/p&gt;

&lt;p&gt;The head-tag script validates every setting against a strict character class before it touches the DOM, and builds the tag with &lt;code&gt;createElement&lt;/code&gt; + &lt;code&gt;setAttribute&lt;/code&gt;, never &lt;code&gt;innerHTML&lt;/code&gt;, so a forum setting can never inject markup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framer: a real plugin with a UI
&lt;/h2&gt;

&lt;p&gt;Framer wanted an actual plugin: a React app that runs inside the editor and writes to the site's custom code via &lt;code&gt;framer.setCustomCode&lt;/code&gt;. This one has a review queue and wants gallery images. It is the most "app-like" of the bunch and the only one where the widget is inserted programmatically rather than declared.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that ate a whole afternoon
&lt;/h2&gt;

&lt;p&gt;The best technical lesson this week had nothing to do with any marketplace. It was email.&lt;/p&gt;

&lt;p&gt;DeskCrew turns inbound support email into tickets. I wanted &lt;code&gt;hello@deskcrew.io&lt;/code&gt; to flow into the product, so I set up Cloudflare Email Routing to forward the domain to my mail pipeline. Every test email vanished. No error, no ticket, nothing.&lt;/p&gt;

&lt;p&gt;The logs finally confessed: &lt;code&gt;dropped automated/loop mail (system-sender:bounces)&lt;/code&gt;. My inbound handler has a loop-guard that drops mail from &lt;code&gt;bounces@&lt;/code&gt;, &lt;code&gt;no-reply@&lt;/code&gt;, &lt;code&gt;mailer-daemon@&lt;/code&gt; and friends, so an auto-responder can never ping-pong with a bounce address forever. Sensible. But &lt;strong&gt;Cloudflare rewrites the envelope sender to &lt;code&gt;bounces@cf-bounce.notify.cloudflare.com&lt;/code&gt; on every forwarded message.&lt;/strong&gt; So the loop-guard was correctly dropping every single forwarded email as if it were a bounce.&lt;/p&gt;

&lt;p&gt;Forwarding services and loop-guards are fundamentally incompatible. The fix was to stop forwarding and point the domain's MX records straight at the mail provider, so the original sender arrives first-hand and the authentication verdicts come through clean. Direct MX, no hop.&lt;/p&gt;

&lt;p&gt;Rule of thumb: &lt;strong&gt;if you forward mail into anything that inspects the sender, the forwarder's envelope rewrite will fight your logic. Prefer direct MX.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually mattered
&lt;/h2&gt;

&lt;p&gt;The widget is one script tag. Shipping it seven ways was seven afternoons of wrappers, review forms, and one genuinely nasty email bug. Building the product was never the bottleneck. Distribution is the work, and it is the part nobody shows you.&lt;/p&gt;

&lt;p&gt;If you run a Framer, Shopify, WordPress, Discourse or docs site and want to see the widget, it is free with no card at &lt;a href="https://deskcrew.io" rel="noopener noreferrer"&gt;deskcrew.io&lt;/a&gt;. And if you have shipped to a marketplace I have not hit yet and know a trap I am about to walk into, I would genuinely love to hear it in the comments.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>webdev</category>
      <category>saas</category>
      <category>showdev</category>
    </item>
    <item>
      <title>A support desk that AI agents can pay to use — no account, no API key, no signup — settling in USDC on Solana.</title>
      <dc:creator>Kaven C</dc:creator>
      <pubDate>Fri, 10 Jul 2026 11:41:10 +0000</pubDate>
      <link>https://dev.to/linknpark/a-support-desk-that-ai-agents-can-pay-to-use-no-account-no-api-key-no-signup-settling-in-usdc-1o9m</link>
      <guid>https://dev.to/linknpark/a-support-desk-that-ai-agents-can-pay-to-use-no-account-no-api-key-no-signup-settling-in-usdc-1o9m</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-07-09"&gt;Weekend Challenge: Passion Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;A support desk that AI agents can pay to use — no account, no API key, no signup — settling in USDC on Solana.&lt;/p&gt;

&lt;p&gt;DeskCrew is an agent-native helpdesk. The unusual part is the front door. An autonomous agent finds the server, calls a tool, and gets back an HTTP 402 Payment Required with machine-readable terms. It pays $0.05 in USDC, retries with a payment header, and gets its answer. There is no human in that loop, and there is no account.&lt;/p&gt;

&lt;p&gt;The goal was to answer a question I couldn't stop thinking about: if agents are going to buy services from each other, what does the checkout look like? Not a card. Not an invoice. Not an OAuth dance that assumes a human with a browser. Something an agent can complete alone, in one round trip, for five cents.&lt;/p&gt;

&lt;p&gt;The passion part is that I don't think identity should be the price of entry. An agent shouldn't have to become somebody to buy one API call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;curl -s -X POST &lt;a href="https://deskcrew.io/api/mcp/deskcrew" rel="noopener noreferrer"&gt;https://deskcrew.io/api/mcp/deskcrew&lt;/a&gt; \&lt;br&gt;
  -H 'Content-Type: application/json' \&lt;br&gt;
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",&lt;br&gt;
       "params":{"name":"draft_support_reply","arguments":{"ticketId":1}}}'&lt;br&gt;
You get a real HTTP 402 with the Solana leg in accepts:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "scheme": "exact",&lt;br&gt;
  "network": "solana",&lt;br&gt;
  "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",&lt;br&gt;
  "payTo": "3uusWWisxWKpscMWAmgV5otpm5Xmjk1W3WPQsHAxFPfa",&lt;br&gt;
  "maxAmountRequired": "50000",&lt;br&gt;
  "feePayer": "EvJZ4f2AUy6BJihdP4cj3EsDaunA9RaDUpWg4nGemqHk"&lt;br&gt;
}&lt;br&gt;
50000 atomic units of USDC = $0.05. Mainnet mint. Real wallet.&lt;/p&gt;

&lt;p&gt;Free tools need no payment at all — search_kb, list_issues, list_changelog. The discovery manifest is public: &lt;a href="https://deskcrew.io/.well-known/x402" rel="noopener noreferrer"&gt;https://deskcrew.io/.well-known/x402&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Site: &lt;a href="https://deskcrew.io" rel="noopener noreferrer"&gt;https://deskcrew.io&lt;/a&gt; · Agent docs: &lt;a href="https://deskcrew.io/agents" rel="noopener noreferrer"&gt;https://deskcrew.io/agents&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/webmilmind1" rel="noopener noreferrer"&gt;
        webmilmind1
      &lt;/a&gt; / &lt;a href="https://github.com/webmilmind1/deskcrew-mcp" rel="noopener noreferrer"&gt;
        deskcrew-mcp
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;DeskCrew — MCP Server&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Agent-native helpdesk. AI agents run real support work over MCP and pay per action in USDC via &lt;a href="https://x402.org" rel="nofollow noopener noreferrer"&gt;x402&lt;/a&gt; — no account, no API key.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://glama.ai/mcp/connectors/io.deskcrew/desk-crew" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/831d9eea8c3f86d08903dbf96367b27ee3cf915308ba3cd9336fe1f673e22e0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f476c616d612d6c69737465642d3633363666313f6c6162656c436f6c6f723d316531623462" alt="Listed on Glama"&gt;&lt;/a&gt;
&amp;nbsp;·&amp;nbsp; &lt;a href="https://smithery.ai/servers/deskcrew/support" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c9c2f8cbf5dc93da99d9fd0872d7dac2bcf85eee8a9821c924b47fe64c4b7e27/68747470733a2f2f736d6974686572792e61692f62616467652f6465736b637265772f737570706f7274" alt="smithery badge"&gt;&lt;/a&gt;
&amp;nbsp;·&amp;nbsp; MCP Registry: &lt;strong&gt;&lt;code&gt;io.deskcrew/support&lt;/code&gt;&lt;/strong&gt; &amp;nbsp;·&amp;nbsp; &lt;a href="https://deskcrew.io" rel="nofollow noopener noreferrer"&gt;deskcrew.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;DeskCrew is a multi-tenant support helpdesk built for AI agents. Humans get a normal dashboard
shared inbox, and email — &lt;strong&gt;agents get a paid MCP door&lt;/strong&gt;. An agent connects over the Model Context
Protocol, lists the available tools, and runs real support work: search and create tickets, search
the knowledge base, draft and post replies, triage and resolve threads — paying per action in USDC.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Connect&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Remote, streamable-HTTP MCP endpoint (nothing to install):&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;https://deskcrew.io/api/mcp/{tenant}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Client config (Claude Desktop, Cursor, or any MCP client):&lt;/p&gt;
&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;{
  &lt;span class="pl-ent"&gt;"mcpServers"&lt;/span&gt;: {
    &lt;span class="pl-ent"&gt;"deskcrew"&lt;/span&gt;: {
      &lt;span class="pl-ent"&gt;"type"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;streamable-http&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
      &lt;span class="pl-ent"&gt;"url"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;https://deskcrew.io/api/mcp/YOUR_TENANT_SLUG&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
    }
  }
}&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Poke the public demo tenant — &lt;code&gt;initialize&lt;/code&gt; and &lt;code&gt;tools/list&lt;/code&gt; are…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/webmilmind1/deskcrew-mcp" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The public MCP connector and agent skill. The desk itself is a private commercial repo, so the snippets below are the load-bearing parts.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The standard. x402 is Coinbase's revival of the long-dead HTTP 402 Payment Required status code. A server answers 402 with signed payment requirements; the client pays and retries with an X-PAYMENT header. It's HTTP semantics, not a protocol bolted on top.&lt;/p&gt;

&lt;p&gt;Why Solana was the interesting chain. The EVM legs use EIP-3009 transferWithAuthorization — the payer signs an off-chain authorization, we relay it. Solana has no such primitive, so x402's exact-svm scheme takes a different shape: the agent builds an SPL TransferChecked transaction and signs it, and our relayer co-signs as fee-payer.&lt;/p&gt;

&lt;p&gt;That detail is the whole reason this feels right on Solana. The paying agent never needs SOL. It holds USDC and nothing else. It doesn't manage a gas balance, doesn't top up, doesn't know what a lamport is. It signs a transfer; we pay for the block space. An agent's wallet becomes single-asset, which is exactly what you want from something that has no hands and no credit card.&lt;/p&gt;

&lt;p&gt;Sub-cent fees matter here too. A $0.05 API call is nonsense on a chain where settlement costs $0.30.&lt;/p&gt;

&lt;p&gt;What I refused to trust. The scheme's own verify/settle does the security-critical binding — it rejects any transaction that doesn't pay the exact amount, to the exact associated token account, in the correct mint, with the fee-payer neither funding the transfer nor appearing in any instruction's accounts, in exactly three instructions. I read that code before I relied on it, then wrote an independent economic guard in front of it anyway, re-checking payTo, asset, network, and that the price is one of the ones we actually advertised. Two locks, different keys.&lt;/p&gt;

&lt;p&gt;Never charge for a failure. Payment settles before a side-effecting tool runs, and a nonce is claimed against a UNIQUE index. If the tool then throws, the pending claim is released and no settlement occurs. An agent is never billed for an error, and a retry can't double-charge. Getting that ordering right took longer than the payment integration.&lt;/p&gt;

&lt;p&gt;The safety layer nobody sees. A paying agent is still an untrusted caller reading untrusted customer text. Every credential is draft-capped: an agent can compose a reply, but sending it requires a human to approve. The tier is read from the credential row, never from the tool arguments — so a prompt injection buried in a support ticket cannot talk the system into escalating itself. That's the structural break in the lethal trifecta: the component holding private data and untrusted content has no external side-effect.&lt;/p&gt;

&lt;p&gt;What I got wrong. For weeks the AI features were silently dead: I'd written the model slug with a hyphen instead of a dot, and every AI surface catches errors and escalates to a human rather than fabricate. So a broken model, a revoked key, and a genuine knowledge-base miss all produced the same customer-facing sentence. Good product behavior; terrible operational signal. The fix was a health probe that distinguishes them. The lesson stuck: no-fabrication escalation is honest to users and lies to operators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;Best Use of Solana.&lt;/p&gt;

&lt;p&gt;Not a token, not an NFT, not a governance contract. A payment rail doing the one job a payment rail should do for machines: settle a five-cent invoice, in a stablecoin, in under a second, from a wallet that holds no gas.&lt;/p&gt;

&lt;p&gt;The exact-svm fee-payer split is what makes it work — the agent brings USDC, the service brings SOL. Anonymous, per-call, no account. Live on mainnet at 3uusWWisxWKpscMWAmgV5otpm5Xmjk1W3WPQsHAxFPfa, verifiable from the manifest above before you send a cent.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>Self-hosting an x402 facilitator: the EIP-3009 gotchas that almost cost us money</title>
      <dc:creator>Kaven C</dc:creator>
      <pubDate>Thu, 09 Jul 2026 07:30:28 +0000</pubDate>
      <link>https://dev.to/linknpark/elf-hosting-an-x402-facilitator-the-eip-3009-gotchas-that-almost-cost-us-money-443k</link>
      <guid>https://dev.to/linknpark/elf-hosting-an-x402-facilitator-the-eip-3009-gotchas-that-almost-cost-us-money-443k</guid>
      <description>&lt;p&gt;We run &lt;a href="https://deskcrew.io" rel="noopener noreferrer"&gt;DeskCrew&lt;/a&gt;, a helpdesk where AI agents can use support tools with no account and no API key — they pay per action in USDC over &lt;a href="https://www.x402.org" rel="noopener noreferrer"&gt;x402&lt;/a&gt;, the HTTP-402 payment standard. An agent calls a tool, gets a &lt;code&gt;402 Payment Required&lt;/code&gt; with an exact quote, signs a USDC authorization, retries, done. Five cents, no signup.&lt;/p&gt;

&lt;p&gt;Most x402 services use a hosted facilitator to verify and settle payments. We self-hosted the whole stack — facilitator, relayer, settlement confirmation — partly for control, partly to support five chains, and partly because I wanted to actually understand what I was trusting. Here's what bit us on the way, so it doesn't bite you.&lt;/p&gt;

&lt;h2&gt;
  
  
  x402 in sixty seconds
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Client calls your paid endpoint with no payment → you respond &lt;code&gt;402&lt;/code&gt; with a quote: price in atomic USDC, your receiving address, network, a timeout.&lt;/li&gt;
&lt;li&gt;Client signs an &lt;strong&gt;EIP-3009&lt;/strong&gt; &lt;code&gt;transferWithAuthorization&lt;/code&gt; — a gasless, off-chain signature authorizing a USDC transfer from their wallet to yours, bound to a unique nonce.&lt;/li&gt;
&lt;li&gt;Client retries with the signed payload in an &lt;code&gt;X-PAYMENT&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;Your facilitator verifies the signature, runs the work, and your relayer broadcasts the authorization on-chain to settle.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple. The gotchas live in steps 2–4.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 1: "nonce consumed" is not "you got paid"
&lt;/h2&gt;

&lt;p&gt;This is the one that matters. Our first settlement-recovery path checked &lt;code&gt;authorizationState(from, nonce)&lt;/code&gt; on the USDC contract — if the nonce was used, we marked the payment settled and the revenue earned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The payer controls that nonce.&lt;/strong&gt; EIP-3009 lets the &lt;em&gt;authorizer&lt;/em&gt; burn their own nonce: they can call &lt;code&gt;cancelAuthorization&lt;/code&gt;, or spend the same nonce in a self-transfer, racing your relayer. Result: the nonce reads "used," your books say "paid," and your wallet received nothing. An adversarial agent could get your tool's output for free, at scale.&lt;/p&gt;

&lt;p&gt;The fix is to confirm the &lt;em&gt;money movement&lt;/em&gt;, not the nonce state. On settlement recovery we now require finding the &lt;code&gt;AuthorizationUsed(authorizer, nonce)&lt;/code&gt; event &lt;strong&gt;and&lt;/strong&gt; verifying the USDC &lt;code&gt;Transfer&lt;/code&gt; log adjacent to it in the same transaction — checking it moved &lt;code&gt;from&lt;/code&gt; the payer, &lt;code&gt;to&lt;/code&gt; our receiving wallet, with &lt;code&gt;value &amp;gt;=&lt;/code&gt; the quoted amount. FiatToken emits these back-to-back, so log-index adjacency pins them to the same authorization. Anything else — missing event, wrong recipient, short amount — fails closed: the row stays unsettled and never counts as revenue.&lt;/p&gt;

&lt;p&gt;Rule of thumb: &lt;strong&gt;any state the payer can influence is not proof of payment. Only the Transfer to your address is.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 2: exactly-once work, payment-after-success
&lt;/h2&gt;

&lt;p&gt;Two invariants pull against each other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never take money for work that failed → &lt;strong&gt;settle only after the tool succeeds.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Never run a side-effecting tool twice for one payment → the client will retry, sometimes concurrently, with the same signed authorization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our answer is a claim table keyed by the EIP-3009 nonce with a UNIQUE constraint. The first request to insert the &lt;code&gt;pending&lt;/code&gt; row wins the race and runs the tool; everyone else carrying the same nonce is a retry and gets refused. If the tool succeeds but settlement fails (RPC hiccup, gas spike), we stamp the row consumed-but-unsettled — the row is &lt;em&gt;never deleted&lt;/em&gt;, so the same authorization can never re-run the action — and a reconcile job retries the on-chain settlement later, using the Gotcha-1 confirmation before it ever promotes the row to revenue.&lt;/p&gt;

&lt;p&gt;The ordering that emerges: &lt;strong&gt;claim the nonce → validate input → run the tool → settle → record.&lt;/strong&gt; Validation before payment matters more than it looks — taking five cents and then returning "invalid input" is a great way to make an agent developer never come back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 3: multi-chain is not copy-paste
&lt;/h2&gt;

&lt;p&gt;"USDC on five chains" sounds like a for-loop. It isn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every chain has a different USDC contract address, and some have bridged-vs-native variants — quote the wrong asset and signatures verify against a token you don't accept.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana has no EIP-3009.&lt;/strong&gt; The whole authorization model changes: a fee-payer co-signs, the flow is transaction-based rather than signature-based, and your "facilitator" logic is effectively a second implementation, not a port.&lt;/li&gt;
&lt;li&gt;Advertise networks honestly in your quotes: a client that picks Sei because your 402 offered it, only to find your relayer has no gas there, burns real goodwill.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're starting out: ship Base only, add chains when someone asks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 4: discovery is table stakes, not demand
&lt;/h2&gt;

&lt;p&gt;We serve &lt;code&gt;/.well-known/x402&lt;/code&gt;, per-workspace manifests, and we're in the MCP registry. Do all of that — it's cheap and it's how agents &lt;em&gt;can&lt;/em&gt; find you. But measure the funnel separately: manifest fetches → 402 quotes issued → payment attempts → settlements. Fetches without quotes are curiosity. Quotes without attempts are checkout friction. Only settled transfers from wallets you don't control are demand. We log all four stages and split "our own test wallets" from strangers, so we can't fool ourselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kick the tires (paid, but we'll pay you back)
&lt;/h2&gt;

&lt;p&gt;The door is live on mainnet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terms: &lt;code&gt;https://deskcrew.io/.well-known/x402&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Quickstart with a copy-paste 402 flow: &lt;a href="https://deskcrew.io/agents" rel="noopener noreferrer"&gt;deskcrew.io/agents&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cheapest calls are $0.02; the fully account-free one is &lt;code&gt;draft_support_reply&lt;/code&gt; at $0.05 — send raw customer text, get a support reply draft back. Payment settles only after the tool succeeds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've got an x402-capable agent: the first 20 external wallets to make a successful paid call get &lt;strong&gt;5× the call price back&lt;/strong&gt; — reply with your tx hash wherever you found this post. Worst case, you're out two cents and got a story.&lt;/p&gt;

&lt;p&gt;Questions about the facilitator internals welcome — happy to go deeper on any of the four gotchas.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>payments</category>
      <category>ai</category>
      <category>agents</category>
    </item>
    <item>
      <title>I built an "agent door" — how AI agents pay per action on my helpdesk (MCP + x402)</title>
      <dc:creator>Kaven C</dc:creator>
      <pubDate>Fri, 03 Jul 2026 13:30:43 +0000</pubDate>
      <link>https://dev.to/linknpark/i-built-an-agent-door-how-ai-agents-pay-per-action-on-my-helpdesk-mcp-x402-3ng8</link>
      <guid>https://dev.to/linknpark/i-built-an-agent-door-how-ai-agents-pay-per-action-on-my-helpdesk-mcp-x402-3ng8</guid>
      <description>&lt;p&gt;Most "AI + payments" stories are about humans buying things through an agent. I wanted the opposite: an &lt;strong&gt;anonymous AI agent that shows up, does one unit of work, and pays for it — no account, no API key, no human in the loop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I built it into a helpdesk. Here's how the "agent door" works, and what I learned wiring HTTP 402 to real settlement.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with giving agents API keys
&lt;/h2&gt;

&lt;p&gt;The default way to let an agent use your API is: a human signs up, adds a card, generates a key, and manages it. That's fine when the agent has an owner and a long relationship with your service.&lt;/p&gt;

&lt;p&gt;It's terrible when the agent is &lt;em&gt;ephemeral&lt;/em&gt; — it needs one action, right now, and will never come back. Onboarding, KYC, key rotation, billing plans… all overhead for a single API call. There's no clean "walk up and pay for exactly what you use" primitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The primitive: HTTP 402 + x402
&lt;/h2&gt;

&lt;p&gt;HTTP has always had a status code reserved for this: &lt;strong&gt;&lt;code&gt;402 Payment Required&lt;/code&gt;&lt;/strong&gt;. It was never really used — until &lt;a href="https://x402.org" rel="noopener noreferrer"&gt;x402&lt;/a&gt; gave it a concrete meaning: the server responds &lt;code&gt;402&lt;/code&gt; with machine-readable payment terms, the client pays (USDC on-chain), and retries the same request with an &lt;code&gt;X-PAYMENT&lt;/code&gt; header. Settlement happens per request.&lt;/p&gt;

&lt;p&gt;No account. No API key. The payer's wallet &lt;em&gt;is&lt;/em&gt; the identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The door
&lt;/h2&gt;

&lt;p&gt;My helpdesk (&lt;a href="https://deskcrew.io" rel="noopener noreferrer"&gt;DeskCrew&lt;/a&gt;) exposes an &lt;strong&gt;MCP server&lt;/strong&gt; per workspace, so an AI agent can operate the desk — search the knowledge base, open and triage tickets, draft and post replies. Read tools are free; write/AI tools are priced.&lt;/p&gt;

&lt;p&gt;You can hit it right now, no signup:&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;-s&lt;/span&gt; https://deskcrew.io/api/mcp/deskcrew &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&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;'{"jsonrpc":"2.0","id":1,"method":"tools/list"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That returns the tool catalog with per-tool prices. Call a &lt;em&gt;paid&lt;/em&gt; tool and you get the challenge:&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;"x402Version"&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;"error"&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 required for tool 'create_ticket'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"accepts"&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;"scheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exact"&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;"maxAmountRequired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"20000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"asset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"payTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxTimeoutSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;…one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;entry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;chain:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;polygon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;avalanche&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;sei&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;solana&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;code&gt;20000&lt;/code&gt; is &lt;code&gt;$0.02&lt;/code&gt; in USDC's 6 decimals. The agent signs a payment authorization for that amount, retries with &lt;code&gt;X-PAYMENT&lt;/code&gt;, and the tool runs once settlement confirms.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually had to get right
&lt;/h2&gt;

&lt;p&gt;The happy path is easy. The interesting part is everything that isn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The amount, recipient, and asset come from the server — never the client.&lt;/strong&gt; The client sends only a &lt;em&gt;signed&lt;/em&gt; authorization. The server builds the payment terms (price from a server-side table, &lt;code&gt;payTo&lt;/code&gt; = a cold wallet, the USDC contract pinned per chain) and verifies the signature satisfies &lt;em&gt;those&lt;/em&gt; terms. Otherwise an agent could "pay" 1 wei, redirect payment to itself, or hand you a fake token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Don't run a side-effecting action before you're paid — but don't double-charge either.&lt;/strong&gt; &lt;code&gt;verify()&lt;/code&gt; only &lt;em&gt;reads&lt;/em&gt; the payer's balance; it's not a reservation. So N concurrent requests from one wallet funded for a single call can all pass verify. I gate it two ways: an &lt;strong&gt;at-most-one-in-flight lease per wallet&lt;/strong&gt;, and a claimed &lt;strong&gt;nonce&lt;/strong&gt; (EIP-3009) inserted &lt;em&gt;before&lt;/em&gt; the action runs, so a replay or a concurrent duplicate can't execute the tool twice for one payment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. For live external effects (like sending a customer an email), settle &lt;em&gt;first&lt;/em&gt;.&lt;/strong&gt; The fairness default is "only charge on success" — so most tools run, then settle. But for the one action where "ran but not charged" is real abuse (a free outbound email from your domain), I flipped the order: confirm payment on-chain, &lt;em&gt;then&lt;/em&gt; send. The safe failure direction is "paid but not delivered," never "delivered for free."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Anonymous ≠ unbounded.&lt;/strong&gt; Per-wallet rate/spend caps, reputation, and a deliverability breaker bound abuse. High-value actions are gated behind &lt;em&gt;earned&lt;/em&gt; reputation, so a day-one anonymous wallet can't email your customers — it gets a draft in a human approval queue instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Self-host the money path.&lt;/strong&gt; I run my own relayer + facilitator, so there's no third party between the agent and settlement. USDC lands in a cold wallet at my prices; even a leaked hot key can only move the payer's funds &lt;em&gt;to my wallet, at my price&lt;/em&gt; (the settle is economically bound).&lt;/p&gt;

&lt;h2&gt;
  
  
  Was it worth it?
&lt;/h2&gt;

&lt;p&gt;For a helpdesk specifically: an agent resolving a support task mid-workflow can now pay for exactly the tools it uses — no account, no key. Whether &lt;em&gt;pay-per-action&lt;/em&gt; beats &lt;em&gt;just issue the agent a key&lt;/em&gt; is genuinely situational, and I'd love the internet's opinion on where the line is.&lt;/p&gt;

&lt;p&gt;But the pattern is bigger than helpdesks. &lt;code&gt;402&lt;/code&gt; + a signed micropayment is the missing "walk up and pay" primitive for the agent web. It's early and rough, but it works today — you just curled it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://deskcrew.io" rel="noopener noreferrer"&gt;deskcrew.io&lt;/a&gt; · the door: &lt;code&gt;POST https://deskcrew.io/api/mcp/deskcrew&lt;/code&gt; · MCP spec + tools: &lt;a href="https://github.com/webmilmind1/deskcrew-mcp" rel="noopener noreferrer"&gt;github.com/webmilmind1/deskcrew-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer anything about the x402 wiring in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>webdev</category>
      <category>crypto</category>
    </item>
  </channel>
</rss>
