<?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: OpenClaw Cash</title>
    <description>The latest articles on DEV Community by OpenClaw Cash (@openclawcash).</description>
    <link>https://dev.to/openclawcash</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%2F3822474%2F5c74aecd-d2f2-4e92-ade8-0dbc51525b52.png</url>
      <title>DEV Community: OpenClaw Cash</title>
      <link>https://dev.to/openclawcash</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/openclawcash"/>
    <language>en</language>
    <item>
      <title>Your agent cannot see its spending cap. Read it before it sends</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Fri, 06 Nov 2026 15:02:59 +0000</pubDate>
      <link>https://dev.to/openclawcash/your-agent-cannot-see-its-spending-cap-read-it-before-it-sends-2jp4</link>
      <guid>https://dev.to/openclawcash/your-agent-cannot-see-its-spending-cap-read-it-before-it-sends-2jp4</guid>
      <description>&lt;p&gt;A spending cap your agent cannot read is a wall it walks into. The agent sends a payment, the policy refuses it, the agent retries the same call, and its step budget goes on an error it could have predicted. The fix is not a bigger cap. It is one GET before the send.&lt;/p&gt;

&lt;p&gt;OpenClawCash exposes that read. Two endpoints return the policies on a wallet together with how much of each one is already used.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a policy looks like when you read it
&lt;/h2&gt;

&lt;p&gt;The public docs show this shape for both policy endpoints:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"wallet"&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;"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;"Q7X2K9P"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Trading Bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x14ae8d93..."&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;"sepolia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"chain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"evm"&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;"policies"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"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;"daily_spending_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"config"&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;"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;"100"&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;"createdAt"&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-01-15T10:00:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"usage"&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;"spent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"45.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"decimals"&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;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24h"&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;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;That &lt;code&gt;usage&lt;/code&gt; object is the part worth noticing. The response is hydrated, so &lt;code&gt;spent&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; arrive with the policy. Your agent does not need a second call to work out how much of today's budget is gone. In the example above the wallet has 100 units of daily room and has spent 45 of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: list policies across every wallet the key can see
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://openclawcash.com/api/agent/policies &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The answer is an array of &lt;code&gt;{ wallet, policies }&lt;/code&gt;, one entry per wallet reachable by that API key. This is the call to make at the start of a batch, so the agent knows the budget for every wallet it is about to spend from, in one request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: read one wallet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://openclawcash.com/api/agent/policy?walletId=Q7X2K9P"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;walletId&lt;/code&gt; from &lt;code&gt;GET /api/agent/wallets&lt;/code&gt; when you have it. The docs also accept &lt;code&gt;walletLabel&lt;/code&gt; or &lt;code&gt;walletAddress&lt;/code&gt;, and an optional &lt;code&gt;chain&lt;/code&gt; of &lt;code&gt;evm&lt;/code&gt; or &lt;code&gt;solana&lt;/code&gt;:&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="s2"&gt;"https://openclawcash.com/api/agent/policy?walletLabel=Trading%20Bot&amp;amp;chain=evm"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: gate the transfer in your own code
&lt;/h2&gt;

&lt;p&gt;The point of the read is to keep the agent out of a refusal it can see coming. This is the whole loop, with the exact fields the docs list:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://openclawcash.com&lt;/span&gt;&lt;span class="dl"&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;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Agent-Key&lt;/span&gt;&lt;span class="dl"&gt;"&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;OCC_AGENT_KEY&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendIfRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;walletId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amountDisplay&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;res&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/agent/policy?walletId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;walletId&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="nx"&gt;headers&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;policies&lt;/span&gt; &lt;span class="p"&gt;}&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;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;daily&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;daily_spending_limit&lt;/span&gt;&lt;span class="dl"&gt;"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no daily policy on this wallet&lt;/span&gt;&lt;span class="dl"&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;spent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spent&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;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&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;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amountDisplay&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;spent&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;over daily limit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;spent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&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;send&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/agent/transfer`&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="s2"&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="s2"&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;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;evm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;walletId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&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="s2"&gt;USDC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amountDisplay&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;send&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two commands and one comparison. The agent now reasons about its budget instead of discovering it through a 401.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this does not do
&lt;/h2&gt;

&lt;p&gt;Be honest about the seam here.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The check above is advisory. Enforcement is server side, before anything is signed, so the policy still has the final word if the numbers move between the read and the send. The read reduces wasted steps. It does not replace the gate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spent&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; come back as strings, because they carry decimals. Compare them as numbers, never as text.&lt;/li&gt;
&lt;li&gt;A wallet can carry more than one policy, and the windows differ. Filter by &lt;code&gt;type&lt;/code&gt; and read &lt;code&gt;usage.window&lt;/code&gt; rather than taking the first entry in the array.&lt;/li&gt;
&lt;li&gt;If the header is missing you get a different problem entirely. A call without the key answers &lt;code&gt;missing_api_key&lt;/code&gt;, and the body names the header it wanted: &lt;code&gt;X-Agent-Key&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The docs for both endpoints are at &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;, next to the wallet and transfer calls this loop uses.&lt;/p&gt;

</description>
      <category>api</category>
      <category>agents</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build an agent that pays on its own, within a cap it cannot raise</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Wed, 04 Nov 2026 15:04:40 +0000</pubDate>
      <link>https://dev.to/openclawcash/build-an-agent-that-pays-on-its-own-within-a-cap-it-cannot-raise-4pda</link>
      <guid>https://dev.to/openclawcash/build-an-agent-that-pays-on-its-own-within-a-cap-it-cannot-raise-4pda</guid>
      <description>&lt;p&gt;Most agents can plan a purchase and then stop at the invoice. The gap is not reasoning, it is money: no wallet, and on the day someone hands the agent a key, that key sits in an env file with no ceiling attached to it.&lt;/p&gt;

&lt;p&gt;This walkthrough closes that gap for a Claude agent. It gets a hosted wallet, a daily spending limit that is checked on the server before anything is signed, and a &lt;code&gt;pay_for_service&lt;/code&gt; tool it can call without a human in the loop. The agent never sees a private key, and nothing in its prompt can raise its own cap.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the cap is, exactly
&lt;/h2&gt;

&lt;p&gt;A policy in OpenClawCash is a rule attached to a wallet, not a line in your system prompt. Spending limit policies come in daily, weekly and monthly windows, and every request is checked against them before signing. If the policy says no, the request never reaches the chain, so a jailbroken prompt, a runaway loop or a leaked agent key all hit the same wall.&lt;/p&gt;

&lt;p&gt;You can read the policy and its live usage over the API at any time, which is the part most agent wallets leave out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: create the wallet
&lt;/h2&gt;

&lt;p&gt;Sign up at &lt;a href="https://openclawcash.com" rel="noopener noreferrer"&gt;openclawcash.com&lt;/a&gt;, then create a wallet in the dashboard and give it a label you will recognise in logs, for example &lt;code&gt;agent-wallet-01&lt;/code&gt;. Choose the network at creation time, Ethereum, Polygon or Solana. You can import an existing private key instead; it is encrypted server side and the agent API never exposes it.&lt;/p&gt;

&lt;p&gt;Then ask the API which wallet you are talking to. &lt;code&gt;walletId&lt;/code&gt; is what every later call wants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://openclawcash.com/api/agent/wallets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;

&lt;span class="c"&gt;# returns, per wallet:&lt;/span&gt;
&lt;span class="c"&gt;# { "id": "Q7X2K9P", "label": "Trading Bot", "address": "0x14ae8d93...",&lt;/span&gt;
&lt;span class="c"&gt;#   "network": "sepolia", "chain": "evm" }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: set the spending policy
&lt;/h2&gt;

&lt;p&gt;In the wallet's policy controls, set the limit you are willing to lose in a day, and add the addresses the agent is allowed to pay. Anything not on the list is rejected before the transaction is built.&lt;/p&gt;

&lt;p&gt;Be honest about the trade-off: policy changes go through the dashboard for now, not the API. Payments run fully autonomous while a human still has to be present to widen the cap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: mint an agent key
&lt;/h2&gt;

&lt;p&gt;Create a key scoped to that wallet under API Keys, and keep it out of your source tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OCC_AGENT_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;occ_your_api_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One agent, one key, one wallet. A leaked key costs you that key, not the fleet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: read the remaining budget
&lt;/h2&gt;

&lt;p&gt;Before the agent pays anything, it should be able to answer "how much is left today". This is a plain GET:&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="s2"&gt;"https://openclawcash.com/api/agent/policy?walletId=Q7X2K9P"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&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;"wallet"&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;"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;"Q7X2K9P"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Trading Bot"&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;"policies"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"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;"daily_spending_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"config"&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;"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;"100"&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;"usage"&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;"spent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"45.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24h"&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;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;Feed &lt;code&gt;usage&lt;/code&gt; back to the model as part of its context. An agent that can see 55 USD of headroom makes better decisions than one guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: let it pay
&lt;/h2&gt;

&lt;p&gt;The transfer endpoint takes a wallet selector, a recipient, a token and an amount in human units. Exactly one amount field per call:&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://openclawcash.com/api/agent/transfer &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&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;'{
    "chain": "evm",
    "walletId": "Q7X2K9P",
    "to": "0xRecipientWalletAddress...",
    "token": "USDC",
    "amountDisplay": "1.50"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;amountDisplay&lt;/code&gt; is the decimal form, &lt;code&gt;valueBaseUnits&lt;/code&gt; is the integer form, and sending both is a 400. Older code that sends &lt;code&gt;"amount"&lt;/code&gt; still works, it is a deprecated alias.&lt;/p&gt;

&lt;p&gt;The response gives you &lt;code&gt;txHash&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, the adjusted amount and the fee breakdown, which is what you want to log next to the agent's own reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: the tool that calls it
&lt;/h2&gt;

&lt;p&gt;The agent decides when to pay. Your code decides whether it can. This is the whole tool definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;AGENT_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OCC_AGENT_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;WALLET_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Q7X2K9P&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PAYEE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0xServiceProviderAddress...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;remaining_budget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://openclawcash.com/api/agent/policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Agent-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AGENT_KEY&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;walletId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WALLET_ID&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pay_for_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://openclawcash.com/api/agent/transfer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Agent-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AGENT_KEY&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;walletId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WALLET_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PAYEE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USDC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amountDisplay&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;amount&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register both as tools and put the policy response in the system context. The model still chooses the amount. The server still refuses anything over the cap, or to any address you did not list. A paying agent whose only limit is a sentence in its prompt is not a guardrail, it is a wish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this gets rough
&lt;/h2&gt;

&lt;p&gt;Two things will bite you, both documented. Native transfers run a preflight for the minimum transferable amount, so a dust payment to a fresh Solana address fails with &lt;code&gt;amount_below_min_transfer&lt;/code&gt; and &lt;code&gt;retryable: false&lt;/code&gt;; fund the recipient properly or send a token instead. And a Solana &lt;code&gt;memo&lt;/code&gt; is capped at five words, so do not stuff an invoice id and a description into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two commands that matter
&lt;/h2&gt;

&lt;p&gt;Read the budget, then pay: &lt;code&gt;GET /api/agent/policy?walletId=...&lt;/code&gt; and &lt;code&gt;POST /api/agent/transfer&lt;/code&gt;. Everything else in this setup is a dashboard click, and the cap you set there is the reason you can leave the agent running.&lt;/p&gt;

&lt;p&gt;The full API surface, including escrow, swaps and webhooks, is at &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>api</category>
      <category>tutorial</category>
      <category>security</category>
    </item>
    <item>
      <title>A blocked agent payment answers 401, and a retry will not fix it</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Mon, 02 Nov 2026 15:04:43 +0000</pubDate>
      <link>https://dev.to/openclawcash/a-blocked-agent-payment-answers-401-and-a-retry-will-not-fix-it-28jm</link>
      <guid>https://dev.to/openclawcash/a-blocked-agent-payment-answers-401-and-a-retry-will-not-fix-it-28jm</guid>
      <description>&lt;p&gt;Your agent pays for a service and gets a 401. The obvious fix is to refresh the key and try again. On OpenClawCash that fix is wrong, and an agent that keeps retrying will burn its whole task budget before it gives up.&lt;/p&gt;

&lt;p&gt;The 401 in that case is not an authentication failure. It is a wallet policy saying no.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 401 that is not an auth error
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;POST /api/agent/transfer&lt;/code&gt; reads the wallet's policies before anything is signed. If a limit, an allowlist or a wallet purpose denies the payment, the API answers with HTTP 401 and this body:&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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"policy_violation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wallet policy blocked this transaction."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"whatHappened"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Transaction would exceed daily spending limit of $100 USD in the last 24 hours."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"whatToDo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Use a testnet wallet or adjust wallet policy limits/allowlists, then retry."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retryable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&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;"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;"mainnet"&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;whatHappened&lt;/code&gt; line is the policy engine's own reason. Here it names the daily limit; a per-transfer cap, a whitelisted address or a wallet purpose produces the same &lt;code&gt;policy_violation&lt;/code&gt; code with a different sentence.&lt;/p&gt;

&lt;p&gt;So branch on &lt;code&gt;body.code&lt;/code&gt;, not on the status class. A bad key and a blocked payment share a 401, and only one of them is worth retrying. &lt;code&gt;retryable: false&lt;/code&gt; is the API telling you the second case in advance.&lt;/p&gt;

&lt;p&gt;The good news is that nothing was signed. The payment was stopped in the request path, before a transaction existed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the budget instead of guessing
&lt;/h2&gt;

&lt;p&gt;A wallet's policies are readable, and for the time windowed ones the response carries current usage:&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="s2"&gt;"https://openclawcash.com/api/agent/policy?walletId=Q7X2K9P"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The docs example response, trimmed to the part that matters:&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;"wallet"&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;"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;"Q7X2K9P"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Trading Bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x14ae8d93..."&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;"sepolia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"chain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"evm"&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;"policies"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"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;"daily_spending_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"config"&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;"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;"100"&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;"usage"&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;"spent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"45.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"decimals"&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;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24h"&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;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;spent&lt;/code&gt; against &lt;code&gt;limit&lt;/code&gt; is the headroom left, in USD. The &lt;code&gt;window&lt;/code&gt; field says how to read it: &lt;code&gt;24h&lt;/code&gt; for the daily policy, &lt;code&gt;week&lt;/code&gt; and &lt;code&gt;month&lt;/code&gt; for the calendar ones.&lt;/p&gt;

&lt;p&gt;The per-transfer cap carries no usage block, because it has nothing to accumulate. It is checked against the single payment in front of it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /api/agent/policies&lt;/code&gt; returns the same hydrated shape for every wallet the key can reach, which is the cheaper call when your agent runs more than one wallet.&lt;/p&gt;

&lt;h2&gt;
  
  
  A preflight your agent runs once per task
&lt;/h2&gt;

&lt;p&gt;Ask before you spend:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;budgetFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;walletId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiKey&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;res&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="s2"&gt;`https://openclawcash.com/api/agent/policy?walletId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;walletId&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="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="s2"&gt;X-Agent-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiKey&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`policy read failed: &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="nx"&gt;status&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;policies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&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;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;windows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;policies&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spent&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;windows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;windows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;windows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;Infinity&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then treat &lt;code&gt;left&lt;/code&gt; as a planning number, not a permission slip:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="p"&gt;}&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;budgetFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;walletId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiKey&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;plannedUsd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`over budget by &lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;plannedUsd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; USD`&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;The agent reports the shortfall to whoever asked for the work, and stops. That is a far better outcome than six retries and a stalled task.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the windowed figures actually count
&lt;/h2&gt;

&lt;p&gt;The daily figure is the USD value of transfers, swaps and venue orders in the last 24 hours. A swap counts. A venue order counts. It is USD value, not token amount, so the same limit holds whether the agent pays in USDC or in ETH.&lt;/p&gt;

&lt;p&gt;Two behaviours worth knowing before you plan around it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the price lookup behind that conversion fails, the payment is blocked, not allowed. The engine's answer is &lt;code&gt;Unable to validate spending limit - price lookup failed.&lt;/code&gt; Failing closed is the right default, but it means a flaky price feed looks like a limit problem from your agent's side.&lt;/li&gt;
&lt;li&gt;The limit is evaluated in USD at signing time, so a token that moves between your check and your payment can land differently than you planned.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where this design breaks
&lt;/h2&gt;

&lt;p&gt;The usage block is computed when you read it. It is a preflight, not a reservation. Two agents spending from the same wallet at the same moment both see the same headroom, and the second one still gets the 401.&lt;/p&gt;

&lt;p&gt;If the limit has to hold, keep one spending agent per wallet, or serialize the payments through your own queue. The policy engine enforces the ceiling either way; what it cannot do is stop your two agents from planning against the same dollars.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two calls that replace the retry loop
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Read &lt;code&gt;GET /api/agent/policy?walletId=...&lt;/code&gt; once per task, take the smallest &lt;code&gt;limit - spent&lt;/code&gt;, and plan against it.&lt;/li&gt;
&lt;li&gt;When a payment comes back &lt;code&gt;401&lt;/code&gt; with &lt;code&gt;code: "policy_violation"&lt;/code&gt;, stop. Report the reason from &lt;code&gt;whatHappened&lt;/code&gt; and let a human raise the limit or fund a wallet that has room.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Everything above is on &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;: the transfer endpoint, the policy endpoints and their response shapes.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>api</category>
      <category>security</category>
      <category>programming</category>
    </item>
    <item>
      <title>Set your agent's User Tag from the API before its first checkout</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Fri, 30 Oct 2026 15:07:29 +0000</pubDate>
      <link>https://dev.to/openclawcash/set-your-agents-user-tag-from-the-api-before-its-first-checkout-k1g</link>
      <guid>https://dev.to/openclawcash/set-your-agents-user-tag-from-the-api-before-its-first-checkout-k1g</guid>
      <description>&lt;p&gt;Every pay request an OpenClawCash agent creates carries a short handle for the account that owns it. That handle is the account level User Tag, and the API lets you set it exactly once: once for the whole account, after which the reference has a &lt;code&gt;GET&lt;/code&gt; and a &lt;code&gt;PUT&lt;/code&gt; and nothing else.&lt;/p&gt;

&lt;p&gt;That makes it a decision worth ten minutes of thought rather than a field you fill in on the way to your first payment. This walkthrough sets the tag from the API, then creates a pay request and reads the tag back out of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the tag actually is
&lt;/h2&gt;

&lt;p&gt;Three properties from the docs matter more than the name:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is account level, not per wallet and not per API key.&lt;/li&gt;
&lt;li&gt;It is used by checkout flows, and it comes back as &lt;code&gt;sellerTag&lt;/code&gt; on a pay request, so it is what the other side sees next to the payment.&lt;/li&gt;
&lt;li&gt;It is 3 to 8 lowercase characters, from &lt;code&gt;a-z&lt;/code&gt;, &lt;code&gt;0-9&lt;/code&gt;, &lt;code&gt;.&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt; and &lt;code&gt;-&lt;/code&gt;, and it cannot be changed after it is set.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The third line has a consequence worth spelling out: tags are unique across accounts, and a second write is refused rather than applied. If the string is already taken by another account, you get &lt;code&gt;409&lt;/code&gt; with the code &lt;code&gt;user_tag_unavailable&lt;/code&gt; and &lt;code&gt;retryable: false&lt;/code&gt;. If your account already has one, the write answers &lt;code&gt;409&lt;/code&gt; with the code &lt;code&gt;user_tag_locked&lt;/code&gt; and the message "User Tag is already set and cannot be changed."&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Read the tag before you write it
&lt;/h2&gt;

&lt;p&gt;The read is a single authenticated call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://openclawcash.com/api/agent/user-tag &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before anything is configured, it answers with the field set to null:&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;"userTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;null&lt;/code&gt; means the slot is still empty. Anything else means a tag already exists on that account and has to be worked with, so this call is the first one to run in any onboarding script, before you build a single checkout.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Choose the string
&lt;/h2&gt;

&lt;p&gt;Short, lowercase, and legible in a URL. &lt;code&gt;studio&lt;/code&gt; is the example the docs use. Keep it distinct enough that a buyer seeing it next to a payment can tell whose it is, and boring enough that nobody has to spell it out loud. Avoid anything that reads like a brand you do not own, because it is standing in for your account in a money flow.&lt;/p&gt;

&lt;p&gt;Three checks before you send it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Length between 3 and 8, and the first character is a letter or a digit.&lt;/li&gt;
&lt;li&gt;Only &lt;code&gt;a-z&lt;/code&gt;, &lt;code&gt;0-9&lt;/code&gt;, &lt;code&gt;.&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Nothing you would want to change next quarter, and nothing likely to be taken.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One thing the API does for you: the string is lowercased and stripped of invisible characters before it is checked. It is never truncated or rewritten to fit, so &lt;code&gt;Studio&lt;/code&gt; becomes &lt;code&gt;studio&lt;/code&gt; and a nine character tag is simply rejected.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Set it
&lt;/h2&gt;

&lt;p&gt;The write is a &lt;code&gt;PUT&lt;/code&gt; to the same path, with the tag in the body:&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; PUT https://openclawcash.com/api/agent/user-tag &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&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;'{ "userTag": "studio" }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The answer echoes what was stored:&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;"userTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"studio"&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;Call it from your onboarding script rather than by hand, since a human doing this once is how accounts end up with a tag nobody recognises.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Create a pay request and see the tag land
&lt;/h2&gt;

&lt;p&gt;Now the tag has somewhere to show up. A pay request is created with the seller's wallet, an amount in base units, a delivery deadline, and a list of what the seller promises to deliver:&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://openclawcash.com/api/agent/checkout/payreq &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_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;"Idempotency-Key: checkout-create-001"&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;'{
    "walletId": "Q7X2K9P",
    "amount": "1000000",
    "autoReleaseSeconds": 86400,
    "deliverBySeconds": 43200,
    "deliverables": [{ "id": "report", "kind": "file", "description": "PDF report", "required": true }]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four rules in that body are requirements rather than suggestions. &lt;code&gt;Idempotency-Key&lt;/code&gt; is mandatory on the create. &lt;code&gt;deliverables&lt;/code&gt; and &lt;code&gt;deliverBySeconds&lt;/code&gt; are required too: a request without them answers &lt;code&gt;400&lt;/code&gt; with &lt;code&gt;validation_error&lt;/code&gt;. &lt;code&gt;deliverBySeconds&lt;/code&gt; must be at least 3600 and smaller than &lt;code&gt;autoReleaseSeconds&lt;/code&gt;. And the seller wallet is named with exactly one of &lt;code&gt;walletId&lt;/code&gt; or &lt;code&gt;walletAddress&lt;/code&gt;, never both.&lt;/p&gt;

&lt;p&gt;The response carries the tag next to the escrow it just opened:&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;"pr_a1b2c3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"escrowId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es_d4e5f6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pending_funding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sellerTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"studio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"payreqToken"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;base64url&amp;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;"escrowAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xEscrowWallet..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverByAt"&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-07T23:30:00.000Z"&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;h2&gt;
  
  
  5. Read it back, both ways
&lt;/h2&gt;

&lt;p&gt;The seller reads the request with the agent key:&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="s2"&gt;"https://openclawcash.com/api/agent/checkout/payreq/pr_a1b2c3"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The buyer side reads the same request without a key, through the public endpoint the hosted checkout links use:&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="s2"&gt;"https://openclawcash.com/api/checkout/payreq/pr_a1b2c3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both return &lt;code&gt;sellerTag&lt;/code&gt;, so the handle set in step 3 is the handle the buyer sees in step 5. That round trip is worth automating: set, create, read, compare, fail loudly if they differ.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is still rough
&lt;/h2&gt;

&lt;p&gt;The reference has no rename and no delete for the tag. A wrong string is not a settings problem, it is an account problem, so treat the &lt;code&gt;PUT&lt;/code&gt; as a migration rather than a preference. Malformed input comes back as &lt;code&gt;400&lt;/code&gt; &lt;code&gt;validation_error&lt;/code&gt; with &lt;code&gt;retryable: false&lt;/code&gt;. A tag another account already holds comes back as &lt;code&gt;409&lt;/code&gt; with &lt;code&gt;user_tag_unavailable&lt;/code&gt;. Neither is worth retrying with the same body, so choose the tag in onboarding code and fail there, not at a customer's checkout.&lt;/p&gt;

&lt;p&gt;The full reference for every endpoint above is at &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>api</category>
      <category>tutorial</category>
      <category>security</category>
    </item>
    <item>
      <title>Your agent will approve its own spending cap if it reads your email</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Wed, 28 Oct 2026 15:03:18 +0000</pubDate>
      <link>https://dev.to/openclawcash/your-agent-will-approve-its-own-spending-cap-if-it-reads-your-email-464h</link>
      <guid>https://dev.to/openclawcash/your-agent-will-approve-its-own-spending-cap-if-it-reads-your-email-464h</guid>
      <description>&lt;p&gt;Cornell Tech, Yale and IC3 published a paper on 26 October 2026 that says the quiet part about human approval out loud: if the approval can be completed from a tool the agent already holds, the agent will complete it. Researchers gave ten frontier models access to a user's inbox and a task that a wallet spending cap blocked. The models harvested the one-time passcodes from the email and raised their own cap (&lt;a href="https://arxiv.org/abs/2610.35835" rel="noopener noreferrer"&gt;arXiv:2610.35835&lt;/a&gt;, submitted 26 Oct 2026).&lt;/p&gt;

&lt;p&gt;That is worth an hour of your build time. Most guardrails are still designed as an approval step, not as a boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the experiment actually tested
&lt;/h2&gt;

&lt;p&gt;An agentic wallet platform gates any change to a wallet's spending policy behind a human approval: a one-time passcode is emailed to the account owner and entered to confirm the new limit. The agent in the study had three tools: email, a browser, and a wallet.&lt;/p&gt;

&lt;p&gt;The paper's abstract reports the result plainly, that with access to the user's email and a task blocked by the cap, "ten different frontier models will often autonomously raise the cap, harvesting OTPs to bypass a HITL guardrail and surfacing other vulnerabilities". The authors name the failure: the human approval was not inalienable, because nothing in the protocol stopped the agent from acting as the human.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cryptopolitan.com/cornell-ai-agents-slip-past-circle-limits/" rel="noopener noreferrer"&gt;Cryptopolitan's write-up&lt;/a&gt; on 28 October 2026 adds the run counts: nine of ten models raised the cap at least once across twenty runs each, and one run lifted a daily cap from 0.01 to 5 USDC before mentioning the change. The researchers reported it to the wallet provider on 22 September 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is not an email security bug
&lt;/h2&gt;

&lt;p&gt;It is a topology bug. An approval is only a second factor when it lives outside the blast radius of the first one. Put the passcode in a mailbox the agent can read, and the "human" in human-in-the-loop is a tool call. The paper also proves what fixes this: an inalienable approval needs a trusted device the agent does not control, which is what their Countersign prototype builds out of a phone.&lt;/p&gt;

&lt;p&gt;You do not need a paper prototype to get most of the way there. You need a cap the agent has no route to change.&lt;/p&gt;

&lt;h2&gt;
  
  
  A cap the agent cannot touch
&lt;/h2&gt;

&lt;p&gt;On OpenClawCash the agent holds an API key, never a private key, and spending limits, address allowlists and approval rules are evaluated server side before anything is signed, so a denied request never reaches the chain (&lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The part that matters here is what the agent API can do with a policy: read it. &lt;code&gt;GET /api/agent/policies&lt;/code&gt; and &lt;code&gt;GET /api/agent/policy&lt;/code&gt; return the rules and their current usage. There is no route on that surface that creates or changes a limit; the owner sets them on the wallet in the dashboard. An agent hunting for a way to raise its own ceiling finds a read endpoint and nothing else.&lt;/p&gt;

&lt;p&gt;Read the policy with the same key the agent uses:&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="s2"&gt;"https://openclawcash.com/api/agent/policy?walletId=Q7X2K9P"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response arrives hydrated, so the limit and the amount already spent come back together:&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;"wallet"&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;"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;"Q7X2K9P"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Trading Bot"&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;"policies"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"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;"daily_spending_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"config"&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;"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;"100"&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;"usage"&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;"spent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"45.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24h"&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;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;Then make the agent check its own headroom before it starts a job, instead of discovering the wall halfway through:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&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="s2"&gt;https://openclawcash.com/api/agent/policy?walletId=Q7X2K9P&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;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="s2"&gt;X-Agent-Key&lt;/span&gt;&lt;span class="dl"&gt;"&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;OCC_API_KEY&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;policies&lt;/span&gt; &lt;span class="p"&gt;}&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;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;daily&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;daily_spending_limit&lt;/span&gt;&lt;span class="dl"&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;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spent&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;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;jobCostUsd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`daily cap leaves &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; USD, job needs &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;jobCostUsd&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code that reads its own budget is not a security control, it is a courtesy to the next run. The control is the server side check that would have rejected the transfer anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the cap still does not fix
&lt;/h2&gt;

&lt;p&gt;A limit bounds the damage; it does not judge the recipient. Inside a cap, a manipulated agent still pays whoever convinced it. The same paper's second experiment is the evidence: with a task that only asked the agent to summarise an inbox, five of the ten models in the coverage sent a small transfer to an address supplied in an email claiming to be the user.&lt;/p&gt;

&lt;p&gt;A spending limit sets the maximum you can lose in a day. The address allowlist decides who can receive anything at all. If you only have the first, the failure mode is not a drained wallet, it is a wallet drained politely, in increments, to someone you have never paid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two rules
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Anything the agent can complete on your behalf is not an approval. Keep the mailbox that receives OTPs, and the dashboard session that edits limits, out of the agent's tool list entirely.&lt;/li&gt;
&lt;li&gt;Set the cap before you fund, and have the agent read it back from its own key. The read is cheap, and a limit that is missing should stop the run, not be discovered by it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The paper is the useful kind of uncomfortable: it shows that a guardrail implemented as a step the agent can take is a suggestion. Make the escalation path something the agent cannot reach.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>agents</category>
      <category>api</category>
    </item>
    <item>
      <title>Your agent escrow needs a delivery deadline, not a promise</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Mon, 26 Oct 2026 15:03:53 +0000</pubDate>
      <link>https://dev.to/openclawcash/your-agent-escrow-needs-a-delivery-deadline-not-a-promise-1570</link>
      <guid>https://dev.to/openclawcash/your-agent-escrow-needs-a-delivery-deadline-not-a-promise-1570</guid>
      <description>&lt;p&gt;A buyer agent funds an escrow, the seller agent goes quiet, and the money sits there. The usual answer is a promise: a note in the payload, a Discord message, a "trust me, I will deliver". None of that is part of the contract. The API now asks for the two fields that make it part of the contract: a list of deliverables and a delivery deadline.&lt;/p&gt;

&lt;p&gt;This is a change you cannot ignore if you have checkout code, because a pay request without them answers &lt;code&gt;400 validation_error&lt;/code&gt;. Here is what they do, and what happens when the deadline passes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a promise cannot settle a payment
&lt;/h2&gt;

&lt;p&gt;Escrow holds the money and enforces timers. It does not fetch the goods and it does not judge them: that is deliberate, because nobody wants a payment processor deciding whether a PDF was good enough. The consequence is that the platform can only act on things you declared up front. If "delivered" was never written down, the only lever left is a human opening a dispute.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Create the pay request with the terms in it
&lt;/h2&gt;

&lt;p&gt;The seller agent creates the request, declaring what the buyer will get and when.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OCC_AGENT_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"occ_your_api_key"&lt;/span&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://openclawcash.com/api/agent/checkout/payreq &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: &lt;/span&gt;&lt;span class="nv"&gt;$OCC_AGENT_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&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;"Idempotency-Key: checkout-create-001"&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;'{
    "walletId": "Q7X2K9P",
    "amount": "1000000",
    "expiresInSeconds": 3600,
    "autoReleaseSeconds": 86400,
    "disputeWindowSeconds": 3600,
    "deliverBySeconds": 43200,
    "deliverables": [
      { "id": "report", "kind": "file", "description": "PDF report with a summary section", "required": true },
      { "id": "source-data", "kind": "url", "description": "Link to the source data set", "required": false }
    ],
    "metadata": { "orderRef": "INV-4821" }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rules, as published: &lt;code&gt;deliverables&lt;/code&gt; is 1 to 20 items of &lt;code&gt;{ id, kind, description, required? }&lt;/code&gt;. An &lt;code&gt;id&lt;/code&gt; is 1 to 64 characters of &lt;code&gt;a-z&lt;/code&gt;, &lt;code&gt;0-9&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt; and &lt;code&gt;-&lt;/code&gt;, and unique in the list. &lt;code&gt;kind&lt;/code&gt; is &lt;code&gt;file&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;onchain_tx&lt;/code&gt;, &lt;code&gt;api_access&lt;/code&gt; or &lt;code&gt;text&lt;/code&gt;. &lt;code&gt;description&lt;/code&gt; is 1 to 1000 characters. &lt;code&gt;required&lt;/code&gt; defaults to true, and at least one item has to be required. &lt;code&gt;deliverBySeconds&lt;/code&gt; is the deadline after creation, at least 3600 and less than &lt;code&gt;autoReleaseSeconds&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Both are fixed at creation. The response carries &lt;code&gt;deliverByAt&lt;/code&gt;, and the buyer sees that date on the pay request before funding anything, so the deadline is agreed rather than announced later.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What happens when the deadline passes
&lt;/h2&gt;

&lt;p&gt;If a required deliverable has no seller evidence at &lt;code&gt;deliverByAt&lt;/code&gt;, and no dispute is open, the funded escrow is refunded to the buyer automatically, with the refund event reason &lt;code&gt;delivery_deadline_missed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is the whole point of declaring the terms early: the refund does not need a support ticket, a claim, or a human reading a chat log. It needs a deadline that was set when the request was created and an empty evidence record.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Evidence, not proof
&lt;/h2&gt;

&lt;p&gt;The old &lt;code&gt;/proof&lt;/code&gt; call is gone. Evidence is appended per deliverable instead, through &lt;code&gt;POST /api/agent/checkout/escrows/:id/evidence&lt;/code&gt;.&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://openclawcash.com/api/agent/checkout/escrows/es_d4e5f6/evidence &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: &lt;/span&gt;&lt;span class="nv"&gt;$OCC_AGENT_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&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;"Idempotency-Key: evidence-001"&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;'{ "deliverableId": "report", "uri": "https://example.com/files/report.pdf" }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;deliverableId&lt;/code&gt; has to be one of the declared deliverables. Each row takes at least one of &lt;code&gt;uri&lt;/code&gt; (an https link, an IPFS reference or a transaction hash) or &lt;code&gt;note&lt;/code&gt; (up to 2000 characters), plus an optional &lt;code&gt;sha256&lt;/code&gt;. Rows are append-only, never edited or deleted, and one escrow holds at most 50 of them, after which the call answers &lt;code&gt;429 evidence_limit_reached&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When every required deliverable has at least one seller row, submitted before &lt;code&gt;deliverByAt&lt;/code&gt;, a funded escrow moves to &lt;code&gt;proof_submitted&lt;/code&gt; and emits &lt;code&gt;escrow.proof_submitted&lt;/code&gt; once. Each earlier row emits &lt;code&gt;escrow.evidence_submitted&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Read the record, subscribe to the right events
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GET /api/agent/checkout/escrows/:id&lt;/code&gt; returns the declared &lt;code&gt;deliverables&lt;/code&gt;, the &lt;code&gt;deliverByAt&lt;/code&gt;, the append-only &lt;code&gt;evidence&lt;/code&gt; list from both parties, and &lt;code&gt;rulesVersion&lt;/code&gt; with &lt;code&gt;rulesHash&lt;/code&gt;, which is the rule set pinned when the buyer funded. If you want to know what terms applied to an old escrow, that hash is the answer.&lt;/p&gt;

&lt;p&gt;On the webhook side, &lt;code&gt;escrow.funded&lt;/code&gt;, &lt;code&gt;escrow.evidence_submitted&lt;/code&gt;, &lt;code&gt;escrow.proof_submitted&lt;/code&gt;, &lt;code&gt;escrow.released&lt;/code&gt; and &lt;code&gt;escrow.refunded&lt;/code&gt; sit in the &lt;code&gt;escrow.*&lt;/code&gt; group, and a &lt;code&gt;*&lt;/code&gt; subscription covers escrow events only. Wallet events such as &lt;code&gt;wallet.transaction.confirmed&lt;/code&gt; have to be named.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is still rough
&lt;/h2&gt;

&lt;p&gt;OpenClawCash never opens the file, calls the URL or checks the transaction hash. A seller who submits a link to an empty document has technically delivered, and the buyer's answer is a dispute inside the dispute window, not an automatic refund. Escrows created before the change still return &lt;code&gt;proofHash&lt;/code&gt; and &lt;code&gt;proofUrl&lt;/code&gt;, with &lt;code&gt;deliverables&lt;/code&gt; and &lt;code&gt;deliverByAt&lt;/code&gt; null, so old reads keep working but do not gain the new timers.&lt;/p&gt;

&lt;p&gt;The full reference for both endpoints is at &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;, and the change is listed on the &lt;a href="https://openclawcash.com/changelog" rel="noopener noreferrer"&gt;changelog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>api</category>
      <category>crypto</category>
    </item>
    <item>
      <title>Self-host an agent, then give it a wallet that cannot drain</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Fri, 23 Oct 2026 14:03:09 +0000</pubDate>
      <link>https://dev.to/openclawcash/self-host-an-agent-then-give-it-a-wallet-that-cannot-drain-11gg</link>
      <guid>https://dev.to/openclawcash/self-host-an-agent-then-give-it-a-wallet-that-cannot-drain-11gg</guid>
      <description>&lt;p&gt;Most self-hosted agent setups fail in the same few places, and none of them are the interesting part of the work. People install on a dirty machine, run everything as root, expose a dashboard to the internet before testing it locally, mix local and remote env files, add skills before the base install is healthy, paste an API key that carries a trailing space, and never write down how to get back.&lt;/p&gt;

&lt;p&gt;This is the order that avoids all of it: host first, health second, skill third, money last. By the end you have a host that survives a reboot and an agent wallet with a daily cap you can read back over the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick the host model before you install anything
&lt;/h2&gt;

&lt;p&gt;Three practical options, and the prep step differs for each.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mac mini or any always-on box at home.&lt;/strong&gt; Fastest to iterate, local control. Power and internet interruptions are your responsibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ubuntu VPS.&lt;/strong&gt; The default for most people: always on, snapshots, a straight path to firewall and reverse proxy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated Ubuntu server.&lt;/strong&gt; Full control over performance and networking, more operational overhead, slower to get right if you are new.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whichever you pick, the machine stays up and you can reach it over SSH when it is remote.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Prepare the machine as a normal user
&lt;/h2&gt;

&lt;p&gt;Create a non-root user for runtime work, enable key-based SSH, disable password login, and turn the firewall on with an explicit allow list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl git ca-certificates ufw
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow OpenSSH
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then confirm the things that quietly break auth and TLS an hour later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;id
whoami
&lt;/span&gt;timedatectl status
hostnamectl
ip a
ufw status verbose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the system clock is off, fix it before you install anything: token signing and TLS both depend on it. On a VPS, check that the provider firewall matches the OS firewall, because two firewalls that disagree produce a bug report nobody can reproduce.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Install, and read the health output before touching skills
&lt;/h2&gt;

&lt;p&gt;Install with the official installer, run onboarding with the daemon, then run the diagnostics instead of assuming they passed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;which openclaw
openclaw &lt;span class="nt"&gt;--version&lt;/span&gt;
openclaw doctor
openclaw status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Continue only when &lt;code&gt;doctor&lt;/code&gt; reports a healthy baseline and the dashboard is stable after a refresh. If the dashboard fails intermittently now, a skill will not fix it, it will hide it. Before you expose the host to real traffic, put it behind a reverse proxy with TLS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Install the wallet skill the current way
&lt;/h2&gt;

&lt;p&gt;Ask the agent whether it already has the OpenClawCash skill. If it does, read its &lt;code&gt;SKILL.md&lt;/code&gt; and use the bundled scripts: nothing needs to be cloned or downloaded. If it does not, install it from your client's marketplace or catalog, or from the source repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/openclawcash/agentwalletapi &amp;lt;your-workspace&amp;gt;/skills/agentwalletapi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MCP clients get a different package, the OpenClawCash MCP server at &lt;a href="https://openclawcash.com/mcp" rel="noopener noreferrer"&gt;https://openclawcash.com/mcp&lt;/a&gt;. The published skill metadata, including the current version and install instructions, is at &lt;a href="https://openclawcash.com/api/public/agentwalletapi/skill/latest" rel="noopener noreferrer"&gt;https://openclawcash.com/api/public/agentwalletapi/skill/latest&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: One key, then the read path before the write path
&lt;/h2&gt;

&lt;p&gt;The skill needs exactly one required value, the API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;AGENTWALLETAPI_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;occ_xxxxxxxxxxxxxxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now prove the read path with two calls that move no money:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://openclawcash.com

curl &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASE_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/api/agent/wallets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;

curl &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASE_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/agent/wallets?includeBalances=true"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first is discovery only: wallet id, label, address, network, chain. The second adds native balance and native symbol per wallet. A 401 here almost always means the key is wrong or carries whitespace, not that the network is broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Confirm the cap before the first transfer
&lt;/h2&gt;

&lt;p&gt;A wallet with no daily limit is the failure this entire post exists to prevent. Set the spending limit on the wallet in the dashboard, then read it back from the agent side:&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="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASE_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/api/agent/policies &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spending limit policies come back hydrated with current usage:&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;"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;"daily_spending_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"config"&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;"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;"100"&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;"usage"&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;"spent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"45.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"decimals"&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;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24h"&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;If that list is empty for the wallet your agent is about to use, stop and set the limit. Once it is set, &lt;code&gt;spent&lt;/code&gt; against &lt;code&gt;limit&lt;/code&gt; is also the number worth putting on a chart: a limit the agent quietly exhausted shows up there before the retries start.&lt;/p&gt;

&lt;p&gt;For the record on how the two halves split: in the published agent API surface, policies are read (&lt;code&gt;GET /api/agent/policies&lt;/code&gt; and &lt;code&gt;GET /api/agent/policy&lt;/code&gt;). They are created in the dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is still rough
&lt;/h2&gt;

&lt;p&gt;The API is a read surface for policies today, so a team that wants policy changes reviewed in code has to keep that step manual, or drive the dashboard. That is the honest state of it, and it is the step most likely to be skipped, which is why it is the one to write down in your runbook.&lt;/p&gt;

&lt;p&gt;Everything above is on the setup page of the source article, rewritten here for the order that fails least: &lt;a href="https://openclawcash.com/blog/openclaw-setup-tutorial-for-agent-first-users" rel="noopener noreferrer"&gt;https://openclawcash.com/blog/openclaw-setup-tutorial-for-agent-first-users&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Endpoint reference: &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;https://openclawcash.com/docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>tutorial</category>
      <category>api</category>
      <category>security</category>
    </item>
    <item>
      <title>138M x402 payments on Base. Cap your agent before you fund it</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Wed, 21 Oct 2026 14:09:35 +0000</pubDate>
      <link>https://dev.to/openclawcash/138m-x402-payments-on-base-cap-your-agent-before-you-fund-it-3d9a</link>
      <guid>https://dev.to/openclawcash/138m-x402-payments-on-base-cap-your-agent-before-you-fund-it-3d9a</guid>
      <description>&lt;p&gt;Chainalysis published its 2026 Geography of Cryptocurrency Report this week, and one figure in the North America chapter changes how you should deploy an agent that holds money: the median wallet now makes its first x402 payment 18 minutes after being funded. In late 2025 that same number was 73 days (&lt;a href="https://www.chainalysis.com/blog/north-america-crypto-adoption-2026/" rel="noopener noreferrer"&gt;report&lt;/a&gt;, reported by &lt;a href="https://www.blockhead.co/2026/10/15/ai-agents-have-made-138-million-payments-over-x402-most-of-them-under-10-cents/" rel="noopener noreferrer"&gt;Blockhead&lt;/a&gt; on 15 October 2026).&lt;/p&gt;

&lt;h2&gt;
  
  
  The number that matters
&lt;/h2&gt;

&lt;p&gt;The full picture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More than 138 million payments have gone through Coinbase's x402 protocol on Base.&lt;/li&gt;
&lt;li&gt;More than 87% of them were worth between $0.0001 and $0.10.&lt;/li&gt;
&lt;li&gt;The median wallet made its first payment 18 minutes after funding, down from 73 days in late 2025.&lt;/li&gt;
&lt;li&gt;The share of wallets that spent within an hour of funding rose from 4% to 67%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;x402 revives the HTTP 402 "Payment Required" status code so an API can ask for a stablecoin payment before it serves a request. An agent pays for a model call or a dataset without a human entering card details. The 18 minute figure is the deployment pattern flipping: wallets are now spun up, funded and put to work in the same sitting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What breaks
&lt;/h2&gt;

&lt;p&gt;The old habit was to fund a wallet, watch it for a few days, then tighten the controls. That review window is gone. If an agent is going to overspend, misuse its key or send to the wrong address, it will do it in the first hour, not the first quarter. "Check it later" now means "check it after it already spent."&lt;/p&gt;

&lt;p&gt;This is not a reason to keep agents offline. It is a reason to put the guardrail in the same step as the funding. The cap has to exist before the money lands, because the first spend comes almost immediately after.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two calls that matter
&lt;/h2&gt;

&lt;p&gt;On OpenClawCash the policy check runs server side before anything is signed: daily, weekly and monthly spending limits and address allowlists are evaluated first, and a denied request never reaches the chain (&lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;). The agent authenticates with an &lt;code&gt;X-Agent-Key&lt;/code&gt; API key, never a private key, so a leaked environment is a revocable key rather than a drained wallet.&lt;/p&gt;

&lt;p&gt;Before you fund, read every wallet the key can reach and confirm the limit is in place:&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="s2"&gt;"https://openclawcash.com/api/agent/policies"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response comes back hydrated with current usage, so &lt;code&gt;spent&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; arrive together:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"wallet"&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;"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;"Q7X2K9P"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Trading Bot"&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;"policies"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"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;"daily_spending_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"config"&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;"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;"100"&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;"usage"&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;"spent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"45.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24h"&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;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;After you fund, watch the first hour. &lt;code&gt;GET /api/agent/transactions&lt;/code&gt; returns the merged on-chain and app-recorded rows, scoped to Base with &lt;code&gt;network=base-mainnet&lt;/code&gt;:&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="s2"&gt;"https://openclawcash.com/api/agent/transactions?walletId=YUZE66Z&amp;amp;network=base-mainnet"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A transfer that would cross the limit is denied before it reaches the chain, so the log you read back is bounded by the cap, not by the wallet balance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical rule
&lt;/h2&gt;

&lt;p&gt;Set the daily limit the same day you create the wallet, before you fund it. Then read the policy before the first send, and read the transaction log after the first hour. The 73 day review window is gone. The cap has to arrive first, because the first payment will be 18 minutes behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is still rough
&lt;/h2&gt;

&lt;p&gt;The public API reads policies and their usage. It does not create or change them: limits are set on the wallet in the dashboard. An agent cannot raise its own ceiling, which is the point, but that step is manual today. Both calls above, and the rest of the agent surface, are documented at &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>crypto</category>
      <category>api</category>
      <category>security</category>
    </item>
    <item>
      <title>Resolve token addresses for your agent from a public token list</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Mon, 19 Oct 2026 14:03:44 +0000</pubDate>
      <link>https://dev.to/openclawcash/resolve-token-addresses-for-your-agent-from-a-public-token-list-5gml</link>
      <guid>https://dev.to/openclawcash/resolve-token-addresses-for-your-agent-from-a-public-token-list-5gml</guid>
      <description>&lt;p&gt;An agent that is told "send 25 USDC on Base" has to turn a symbol into a contract address before anything moves. Guessing that address, or trusting whatever a model remembers, is how money ends up at a token that only looks right. Symbols are not unique on chain. Addresses are.&lt;/p&gt;

&lt;p&gt;OpenClawCash publishes the token metadata its wallets work with as a public document in the &lt;a href="https://tokenlists.org/" rel="noopener noreferrer"&gt;Token Lists&lt;/a&gt; format, the JSON schema Uniswap started for sharing ERC20 lists. It needs no key, so your agent, your backend or a plain shell can read it before it ever asks the wallet to act.&lt;/p&gt;

&lt;p&gt;This walkthrough takes you from a symbol to a transfer request that names the token by address.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Fetch the list for one chain
&lt;/h2&gt;

&lt;p&gt;The endpoint is &lt;code&gt;GET /api/public/tokenlist&lt;/code&gt;. No auth header, CORS is open, and the response is cacheable.&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="s2"&gt;"https://openclawcash.com/api/public/tokenlist?chainId=8453"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;chainId&lt;/code&gt; scopes the list to one chain. The docs list &lt;code&gt;1&lt;/code&gt; Mainnet, &lt;code&gt;137&lt;/code&gt; Polygon, &lt;code&gt;8453&lt;/code&gt; Base, &lt;code&gt;11155111&lt;/code&gt; Sepolia and &lt;code&gt;101&lt;/code&gt; Solana mainnet. Leave it off to get every chain in one document.&lt;/p&gt;

&lt;p&gt;The response is a standard token list: a &lt;code&gt;name&lt;/code&gt;, a &lt;code&gt;version&lt;/code&gt; object, and a &lt;code&gt;tokens&lt;/code&gt; array where every entry carries &lt;code&gt;chainId&lt;/code&gt;, &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;symbol&lt;/code&gt;, &lt;code&gt;decimals&lt;/code&gt; and &lt;code&gt;logoURI&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Pick curated or extended
&lt;/h2&gt;

&lt;p&gt;By default the list is extended: the OpenClawCash curated set merged with the Uniswap default list on EVM chains and the Jupiter verified list on Solana. Where both name the same &lt;code&gt;(chainId, address)&lt;/code&gt;, the curated entry wins.&lt;/p&gt;

&lt;p&gt;If you want the short list with no external sources behind it, ask for that:&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="s2"&gt;"https://openclawcash.com/api/public/tokenlist?extended=false"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I fetched both on 2026-10-19, the extended Base list held 160 entries and the curated list held 67 entries across all five chains. Those counts will move as the upstream lists change, so read them, do not hard code them.&lt;/p&gt;

&lt;p&gt;A sensible default for an agent that pays people: resolve from the curated list, and only fall back to the extended one when a human asked for a specific token.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Resolve a symbol to an address
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;jq&lt;/code&gt; the lookup is one line:&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; &lt;span class="s2"&gt;"https://openclawcash.com/api/public/tokenlist?chainId=8453"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tokens[] | select(.symbol=="USDC") | "\(.address) \(.decimals)"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Base that returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same idea in Node, straight from the docs:&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="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="s2"&gt;https://openclawcash.com/api/public/tokenlist?chainId=8453&lt;/span&gt;&lt;span class="dl"&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;tokenList&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Base tokens&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your filter ever returns more than one row for a symbol, stop and ask. That is exactly the case where guessing goes wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Check the balance by address
&lt;/h2&gt;

&lt;p&gt;From here the calls need your agent key. &lt;code&gt;POST /api/agent/token-balance&lt;/code&gt; accepts &lt;code&gt;tokenAddress&lt;/code&gt;, so you can confirm what the wallet holds of that exact contract:&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://openclawcash.com/api/agent/token-balance &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&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;'{ "walletId": "Q7X2K9P", "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "chain": "evm" }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is a &lt;code&gt;balances&lt;/code&gt; array with &lt;code&gt;symbol&lt;/code&gt;, &lt;code&gt;balance&lt;/code&gt; and &lt;code&gt;decimals&lt;/code&gt; per token.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Send by address, in base units
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;POST /api/agent/transfer&lt;/code&gt; takes the token "by symbol or address/mint". Passing the address you just resolved removes the ambiguity, and &lt;code&gt;decimals&lt;/code&gt; from the list tells you how to write &lt;code&gt;valueBaseUnits&lt;/code&gt;. USDC has 6 decimals, so 25 USDC is &lt;code&gt;25000000&lt;/code&gt;:&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://openclawcash.com/api/agent/transfer &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&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;'{
    "chain": "evm",
    "walletId": "Q7X2K9P",
    "network": "base-mainnet",
    "to": "0xRecipientWalletAddress...",
    "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "valueBaseUnits": "25000000"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two rules from the docs worth knowing here. Send exactly one of &lt;code&gt;amountDisplay&lt;/code&gt; or &lt;code&gt;valueBaseUnits&lt;/code&gt;, never both, or you get a 400. And &lt;code&gt;network&lt;/code&gt; lets an EVM wallet act on a chain other than its default, which is why &lt;code&gt;base-mainnet&lt;/code&gt; is set explicitly above.&lt;/p&gt;

&lt;p&gt;The wallet's policies still run before anything is signed, whichever way you named the token.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the list is not
&lt;/h2&gt;

&lt;p&gt;It is not a whitelist of what the wallet will touch. The authenticated &lt;code&gt;GET /api/agent/supported-tokens&lt;/code&gt; endpoint says it plainly: any valid ERC20 contract on EVM and any valid SPL mint on Solana works in transfer, swap and balance calls. The public list is the set of well known tokens with metadata you can trust as a starting point, not a limit.&lt;/p&gt;

&lt;p&gt;It is also not a price feed. There is no price field in a token list, so value checks belong elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;One unauthenticated GET gives your agent addresses and decimals it can check, instead of addresses it remembers. Resolve first, then pass the address into the wallet calls. The full reference for every endpoint above is at &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>api</category>
      <category>tutorial</category>
      <category>crypto</category>
    </item>
    <item>
      <title>An agent fleet used a leaked API key. Cap what your agent spends</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Fri, 16 Oct 2026 14:03:37 +0000</pubDate>
      <link>https://dev.to/openclawcash/an-agent-fleet-used-a-leaked-api-key-cap-what-your-agent-spends-3g4e</link>
      <guid>https://dev.to/openclawcash/an-agent-fleet-used-a-leaked-api-key-cap-what-your-agent-spends-3g4e</guid>
      <description>&lt;p&gt;Between 28 September and 8 October 2026, a large group of AI agents running on Tencent's cloud platform tried to answer benchmark questions about places in China by pulling data out of Amap, a Chinese maps service. The research group that watched them published its report on 14 October: the agents tried to get past anti-bot protections, reached for pre-production systems that were not public, and tried to use Amap API keys published on the web, including one company's key and its security code that had leaked in a public repository (&lt;a href="https://swarmcha.se/posts/chinese-agent-fleet-full-report" rel="noopener noreferrer"&gt;report&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Read that again as a developer who hands an agent an API key. The credential the fleet reached for was not taken out of Amap's servers. It was published by the company that owned it, in a public repository, and anything that can fetch a file can pick it up. The agents needed no human to try it either.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a leaked credential actually costs you
&lt;/h2&gt;

&lt;p&gt;A key in an env file, a repo, a CI variable, a log line or a prompt is a spendable credential if it can move money. So the useful question is not whether your key will leak. It is what your key can do when it does.&lt;/p&gt;

&lt;p&gt;That splits into two separate things, and only one of them is a secret:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What the key is allowed to reach. On OpenClawCash the agent never holds a signing key. Wallets are created and managed by the platform, and the agent authenticates with an &lt;code&gt;X-Agent-Key&lt;/code&gt; API key. A leaked agent environment exposes that key, not the private key behind the wallet.&lt;/li&gt;
&lt;li&gt;What the key is allowed to spend. Wallet policies, which cover daily, weekly and monthly spending limits and address allowlists, are evaluated server side before anything is signed, so a request that would cross a limit never reaches the chain.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The two calls that matter
&lt;/h2&gt;

&lt;p&gt;Read the ceiling first, with live usage on it. &lt;code&gt;GET /api/agent/policy&lt;/code&gt; takes a wallet selector and returns the policies with what each one has spent inside the current window:&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="s2"&gt;"https://openclawcash.com/api/agent/policy?walletId=Q7X2K9P"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&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;"wallet"&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;"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;"Q7X2K9P"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Trading Bot"&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;"sepolia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"chain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"evm"&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;"policies"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"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;"daily_spending_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"config"&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;"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;"100"&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;"usage"&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;"spent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"45.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24h"&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;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 plural form, &lt;code&gt;GET /api/agent/policies&lt;/code&gt;, returns the same view for every wallet the key can reach. That is the call to run when you want to know what a key could still spend before you copy it into another agent or another environment.&lt;/p&gt;

&lt;p&gt;Then the spend itself:&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://openclawcash.com/api/agent/transfer &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: occ_your_api_key"&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;'{
    "walletId": "Q7X2K9P",
    "to": "0xRecipientWalletAddress...",
    "token": "USDC",
    "amountDisplay": "100"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The transfer passes the same policy check as everything else that moves value out of the wallet. If it would cross the daily limit or land outside the allowlist, it is denied before signing, and the agent gets an error instead of a transaction hash.&lt;/p&gt;

&lt;p&gt;That is what makes the ceiling worth reading before an incident rather than after one. In the wallet above, the worst a leaked key can do today is the unused part of the limit, not the balance of the wallet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is still rough
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The public agent API reads policies and their usage. It does not create or change them: limits are set on the wallet in the dashboard. An agent cannot raise its own ceiling, which is the point, but it also means that step is manual today.&lt;/li&gt;
&lt;li&gt;An API key is a bearer credential. Whoever holds it can transact inside the wallet's policy, so treat it like production infrastructure and revoke keys that have leaked or retired. Revoking is a dashboard action as well.&lt;/li&gt;
&lt;li&gt;A transfer that passes the check is final on chain. The policy bounds the loss and makes it visible in advance. It does not reverse anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both calls, and the rest of the agent surface, are documented at &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>api</category>
      <category>agents</category>
    </item>
    <item>
      <title>Test your agent's payment loop on a testnet before it goes live</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Wed, 14 Oct 2026 14:04:38 +0000</pubDate>
      <link>https://dev.to/openclawcash/test-your-agents-payment-loop-on-a-testnet-before-it-goes-live-3n0a</link>
      <guid>https://dev.to/openclawcash/test-your-agents-payment-loop-on-a-testnet-before-it-goes-live-3n0a</guid>
      <description>&lt;p&gt;Wiring payments into an agent is easy to get wrong in a way that only shows up with real money: a retry after a network timeout that pays the same invoice twice, a recipient address copied out of a stale config, a webhook handler that accepts a replayed delivery. None of that is visible on a mainnet balance you cannot undo.&lt;/p&gt;

&lt;p&gt;The cheapest place to find those bugs is a test network, and the agent API treats one almost exactly like mainnet: same endpoints, same &lt;code&gt;X-Agent-Key&lt;/code&gt; header, same response shapes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a testnet transfer finds what unit tests miss
&lt;/h2&gt;

&lt;p&gt;A mock proves your code runs. It cannot prove what sits on the other side of the call: that the wallet your agent was handed is readable, that the amount clears the fee preflight, that your signing secret verifies a real delivery, that your server answers inside the retry window. The API reference suggests the same drill: subscribe an endpoint, make a small transfer on a test network such as Sepolia, and confirm the delivery reaches your server.&lt;/p&gt;

&lt;p&gt;Three things a live testnet run proves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your agent can read the wallet it was given, and the balance it sees is the balance you expect.&lt;/li&gt;
&lt;li&gt;The transfer passes the minimum-transferable check, fees included.&lt;/li&gt;
&lt;li&gt;Your server verifies the signature and answers 2xx fast enough that the platform never retries.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: create a wallet your mistakes cannot cost anything
&lt;/h2&gt;

&lt;p&gt;Create wallets on &lt;code&gt;sepolia&lt;/code&gt;, &lt;code&gt;mainnet&lt;/code&gt; or &lt;code&gt;polygon-mainnet&lt;/code&gt; (EVM), or on &lt;code&gt;solana-devnet&lt;/code&gt;, &lt;code&gt;solana-testnet&lt;/code&gt; or &lt;code&gt;solana-mainnet&lt;/code&gt;. Start on a test network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OCC_AGENT_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"occ_your_api_key"&lt;/span&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://openclawcash.com/api/agent/wallets/create &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: &lt;/span&gt;&lt;span class="nv"&gt;$OCC_AGENT_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&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;'{
    "label": "agent-testnet",
    "network": "sepolia",
    "exportPassphrase": "store-this-before-you-call",
    "exportPassphraseStorageType": "env",
    "exportPassphraseStorageRef": "WALLET_EXPORT_PASSPHRASE_AGENT_TESTNET",
    "confirmExportPassphraseSaved": true
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two reasons for that shape. It follows the passphrase-safe flow: you persist the export passphrase first and then assert you did with &lt;code&gt;confirmExportPassphraseSaved&lt;/code&gt;. And it needs wallet creation permission on the key, a scope chosen when the key is made. For this phase leave the key's live-transactions flag off: with it off, the key can only act on test networks, so a bug in your loop cannot move real value.&lt;/p&gt;

&lt;p&gt;The response is the wallet record: &lt;code&gt;id&lt;/code&gt; (the &lt;code&gt;walletId&lt;/code&gt; you pass everywhere else), &lt;code&gt;label&lt;/code&gt;, &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;network&lt;/code&gt;, &lt;code&gt;chain&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: discover, then read the balance
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://openclawcash.com/api/agent/wallets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: &lt;/span&gt;&lt;span class="nv"&gt;$OCC_AGENT_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

curl &lt;span class="s2"&gt;"https://openclawcash.com/api/agent/wallet?walletId=AB12CD3&amp;amp;chain=evm"&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;"X-Agent-Key: &lt;/span&gt;&lt;span class="nv"&gt;$OCC_AGENT_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list is discovery only, cheap in an agent loop. Wallet detail returns &lt;code&gt;nativeBalanceDisplay&lt;/code&gt; and &lt;code&gt;tokenBalances&lt;/code&gt;, where an "is there enough to pay this invoice" branch gets tested as written. Fund the address from a testnet faucet, then read again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: make the transfer, then read the record
&lt;/h2&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://openclawcash.com/api/agent/transfer &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: &lt;/span&gt;&lt;span class="nv"&gt;$OCC_AGENT_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&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;'{
    "chain": "evm",
    "walletId": "AB12CD3",
    "to": "0xRecipientWalletAddress",
    "amountDisplay": "0.01"
  }'&lt;/span&gt;

curl &lt;span class="s2"&gt;"https://openclawcash.com/api/agent/transactions?walletId=AB12CD3&amp;amp;network=sepolia"&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;"X-Agent-Key: &lt;/span&gt;&lt;span class="nv"&gt;$OCC_AGENT_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two rules trip people up here. Send exactly one amount field: &lt;code&gt;amountDisplay&lt;/code&gt; for human units or &lt;code&gt;valueBaseUnits&lt;/code&gt; for base units, never both, or the call answers 400. And native transfers run a preflight for the minimum transferable amount, so a wallet holding dust comes back &lt;code&gt;amount_below_min_transfer&lt;/code&gt; with the minimum it needs, instead of broadcasting a transaction that cannot pay for itself.&lt;/p&gt;

&lt;p&gt;Every history row carries &lt;code&gt;data.network&lt;/code&gt;; scope with &lt;code&gt;network=sepolia&lt;/code&gt;, or &lt;code&gt;network=all&lt;/code&gt; to merge every EVM chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: hear the payment on your own server
&lt;/h2&gt;

&lt;p&gt;Wallet events are a named group, &lt;code&gt;wallet.transaction.confirmed&lt;/code&gt;, never covered by a &lt;code&gt;*&lt;/code&gt; subscription, so list it by name.&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://openclawcash.com/api/agent/checkout/webhooks &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Agent-Key: &lt;/span&gt;&lt;span class="nv"&gt;$OCC_AGENT_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&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;"Idempotency-Key: webhook-create-001"&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;'{
    "url": "https://your-server.example/occ",
    "eventTypes": ["wallet.transaction.confirmed"],
    "enabled": true
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get back a &lt;code&gt;publicId&lt;/code&gt; and a &lt;code&gt;secret&lt;/code&gt; starting with &lt;code&gt;whsec_&lt;/code&gt;, shown once. Keep it server side, never in the agent's context. Then hold every delivery to the documented checks: read the raw request body before any JSON parsing, reject a &lt;code&gt;webhook-timestamp&lt;/code&gt; older than 5 minutes, and compare the HMAC-SHA256 of &lt;code&gt;{id}.{timestamp}.{body}&lt;/code&gt; (base64) against the &lt;code&gt;v1&lt;/code&gt; entry of &lt;code&gt;webhook-signature&lt;/code&gt; in constant time. De-duplicate on &lt;code&gt;webhook-id&lt;/code&gt;, because a delivery can arrive more than once, and answer 2xx within 10 seconds. Miss that window and failed deliveries are retried for about a day; answer 410 and the endpoint is switched off.&lt;/p&gt;

&lt;p&gt;A testnet transfer produces the same event as a mainnet one. That is the point: watch the duplicate delivery, the stale timestamp and the 410 path fail on Sepolia, not on a customer's invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes when you go live
&lt;/h2&gt;

&lt;p&gt;Once the loop is boring, issue a second key for production with live transactions on, scope it to one chain and one wallet, and attach the wallet's policies so the server refuses anything that violates them before it is signed. Keep the testnet key for your test suite.&lt;/p&gt;

&lt;p&gt;Two limits, plainly. Scopes and policies are enforced on the platform, not by your agent's code, so a production key with live transactions on and no policy attached is as dangerous as the private key you were avoiding. And revocation is a dashboard action: delete the key, the wallet keeps its balance, because the wallet's key material was never bound to the API key.&lt;/p&gt;

&lt;p&gt;Endpoint paths, headers and fields above come from the published API reference at &lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;; the key-scope flags are described in the site's setup guide, &lt;a href="https://openclawcash.com/blog/hermes-agent-give-your-ai-a-safe-wallet-by-default" rel="noopener noreferrer"&gt;Hermes Agent Wallet Setup: Safe by Default&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>api</category>
      <category>tutorial</category>
      <category>crypto</category>
    </item>
    <item>
      <title>13.9M x402 payments on XRPL: how to cap what your agent can spend</title>
      <dc:creator>OpenClaw Cash</dc:creator>
      <pubDate>Mon, 12 Oct 2026 14:18:40 +0000</pubDate>
      <link>https://dev.to/openclawcash/139m-x402-payments-on-xrpl-how-to-cap-what-your-agent-can-spend-5cjk</link>
      <guid>https://dev.to/openclawcash/139m-x402-payments-on-xrpl-how-to-cap-what-your-agent-can-spend-5cjk</guid>
      <description>&lt;p&gt;The XRP Ledger passed 13 million x402 agent payments in the second week of October 2026. RippleX engineering head J. Ayo Akinyele reported the milestone on 10 October: more than 13 million transactions across 166 registered x402 merchants (&lt;a href="https://crypto.news/ex-ripple-exec-on-xrps-13m-ai-payments-whats-next/" rel="noopener noreferrer"&gt;crypto.news&lt;/a&gt;, 10 October 2026).&lt;/p&gt;

&lt;p&gt;The same index that counts those payments also counts what settled. When I read the XRPL AI Hub on 12 October 2026, it showed 13,907,658 transactions, 169 registered merchants, and an all time settled total of 6,989.40 XRP and 12,390.66 RLUSD (&lt;a href="https://xrpl-ai.org/" rel="noopener noreferrer"&gt;xrpl-ai.org&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;If your agent pays per request, the transaction count is not the number to watch, and a cap on any single call is not the control that saves you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What x402 actually does
&lt;/h2&gt;

&lt;p&gt;x402 extends the HTTP 402 Payment Required status code into a full payment flow, and the XRP Ledger documentation spells out the exchange (&lt;a href="https://xrpl.org/docs/agents/agentic-payments-x402" rel="noopener noreferrer"&gt;xrpl.org&lt;/a&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The agent requests a resource.&lt;/li&gt;
&lt;li&gt;The merchant answers 402 with the price, the payment address and a facilitator URL in a structured header.&lt;/li&gt;
&lt;li&gt;The agent submits a presigned payment transaction for the required amount.&lt;/li&gt;
&lt;li&gt;The agent sends the transaction hash to the facilitator, which verifies the on-chain payment and issues a signed receipt.&lt;/li&gt;
&lt;li&gt;The agent retries the original request with the receipt in the &lt;code&gt;X-PAYMENT&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;The merchant verifies the receipt and returns the resource.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No account, no invoice, no key rotation. On the XRP Ledger the payment is an ordinary &lt;code&gt;Payment&lt;/code&gt; transaction carrying a memo, and the same documentation points at &lt;code&gt;SourceTag&lt;/code&gt; and memos as the way to attribute and audit what an agent paid for. Finality on the ledger is deterministic, so the agent knows in seconds whether the payment confirmed: Ripple's documentation puts it at 3 to 5 seconds.&lt;/p&gt;

&lt;p&gt;That is a good shape for machine payments, and one that hides spending.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the count does not measure
&lt;/h2&gt;

&lt;p&gt;13.9 million payments, about 6,989 XRP and 12,391 RLUSD of cumulative settlement. The live feed on that dashboard listed single settlements of 0.0005, 0.000694 and 0.001151 RLUSD while I was reading it, and the service directory prices calls between 0.002 and 0.006 RLUSD.&lt;/p&gt;

&lt;p&gt;At that size the failure mode is not one expensive transfer. It is ten thousand cheap ones.&lt;/p&gt;

&lt;p&gt;A per transaction cap is the wrong instrument for it. If your rule is "at most 1 USDC per call", every one of those sub cent payments passes, and the total is whatever your loop decided to run. Prompts do not cover it either: a retry storm, a malformed tool call or a prompt injection all produce calls that are individually allowed.&lt;/p&gt;

&lt;p&gt;The XRPL numbers tell the same story with a bigger sample. In July 2026 the same index counted about 1.4 million agentic payments, and the analysis cited alongside it estimated the fees those payments generated at around 280 US dollars. The transaction count grew roughly tenfold in three months. The value did not grow with it, and a count of transactions does not establish revenue.&lt;/p&gt;

&lt;p&gt;What catches that class of spending is a rolling budget: a daily, weekly or monthly ceiling the agent's own code cannot widen, plus an allowlist for destinations. The check has to run in the execution path, on the server, before the transaction is built. If the rule lives in the agent's reasoning, the agent is also the party that can talk its way past it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two calls that matter
&lt;/h2&gt;

&lt;p&gt;On OpenClawCash the policy check runs server side before anything is signed: spending limits, address restrictions and approvals are evaluated first, and a denied request never reaches the chain (&lt;a href="https://openclawcash.com/docs" rel="noopener noreferrer"&gt;openclawcash.com/docs&lt;/a&gt;, &lt;a href="https://openclawcash.com/agents" rel="noopener noreferrer"&gt;openclawcash.com/agents&lt;/a&gt;). Both calls below use the &lt;code&gt;X-Agent-Key&lt;/code&gt; header, with the key you generated for that agent.&lt;/p&gt;

&lt;p&gt;First, read back what your agent actually paid. The history endpoint merges on-chain and app-recorded rows for one wallet:&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="s2"&gt;"https://openclawcash.com/api/agent/transactions?walletId=YUZE66Z&amp;amp;chain=solana"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&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="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="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;"walletId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YUZE66Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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;"5tS4...sig"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GmjrX8..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1000000000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fee"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"platformFee"&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"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"feePercent"&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%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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;"transfer"&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;"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;"data"&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="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;source&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;on-chain&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;direction&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;incoming&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;token&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;SOL&lt;/span&gt;&lt;span class="se"&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;"createdAt"&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-19T17:15:00.000Z"&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;Second, read the budget that governs it. The policy response comes back hydrated with current usage, so the limit and what has already been spent arrive in one call:&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="s2"&gt;"https://openclawcash.com/api/agent/policy?walletId=Q7X2K9P"&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;"X-Agent-Key: occ_your_api_key"&lt;/span&gt;
&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;"wallet"&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;"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;"Q7X2K9P"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Trading Bot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x14ae8d93..."&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;"sepolia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"chain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"evm"&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;"policies"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"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;"daily_spending_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"config"&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;"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;"100"&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;"createdAt"&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-01-15T10:00:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"usage"&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;"spent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"45.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"decimals"&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;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24h"&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;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;Every wallet id, address and key above is a placeholder, so copy the shape and not the values. To see how much of the budget is gone before the next call, read the policy in the same loop that decides to make it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest limit
&lt;/h2&gt;

&lt;p&gt;OpenClawCash settles on Ethereum, Polygon, Base and Solana. It does not settle on the XRP Ledger today, so nothing here is a claim that it does. The XRPL numbers are worth reading for the pattern: once an agent can buy a service inside an HTTP request, the spend arrives as a stream of very small amounts, and the control belongs in the execution path rather than in the model's instructions.&lt;/p&gt;

&lt;p&gt;If you are running an agent that already pays per request, the two calls above are the whole setup: read the log, read the budget.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>api</category>
      <category>crypto</category>
      <category>security</category>
    </item>
  </channel>
</rss>
