<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type='text/xsl' href='rfc2629.xslt' ?>
<rfc
  xmlns:xi="http://www.w3.org/2001/XInclude"
  category="info"
  docName="draft-fluid-fadp-00"
  ipr="trust200902"
  obsoletes=""
  updates=""
  submissionType="independent"
  xml:lang="en"
  tocInclude="true"
  tocDepth="4"
  symRefs="true"
  sortRefs="true"
  version="3">

  <front>
    <title abbrev="FADP">
      Fluid Agentic DeFi Protocol (FADP/1.0): HTTP-Native Micropayment Authentication for Autonomous AI Agents
    </title>

    <seriesInfo name="Internet-Draft" value="draft-fluid-fadp-00"/>

    <author fullname="Abhijeeth Ganji" initials="A." surname="Ganji">
      <organization>Fluid Wallet</organization>
      <address>
        <email>fluidbase9@gmail.com</email>
        <uri>https://fluidnative.com</uri>
      </address>
    </author>

    <date year="2026" month="April" day="21"/>

    <area>Applications and Real-Time Area</area>
    <workgroup>Independent Submission</workgroup>

    <keyword>HTTP</keyword>
    <keyword>402</keyword>
    <keyword>payment</keyword>
    <keyword>AI agent</keyword>
    <keyword>autonomous agent</keyword>
    <keyword>micropayment</keyword>
    <keyword>blockchain</keyword>
    <keyword>USDC</keyword>
    <keyword>stablecoin</keyword>
    <keyword>DeFi</keyword>
    <keyword>machine-to-machine payment</keyword>

    <abstract>
      <t>
        This document defines the Fluid Agentic DeFi Protocol (FADP), version 1.0.
        FADP is an application-layer protocol layered atop HTTP that enables
        autonomous AI agents to pay for access to web resources using on-chain
        cryptocurrency transfers, with cryptographic proof of payment embedded
        directly in HTTP headers.
      </t>
      <t>
        FADP extends HTTP 402 (Payment Required) with two new header fields:
        <tt>X-FADP-Required</tt>, which a server uses to communicate payment
        terms to an agent, and <tt>X-FADP-Proof</tt>, which an agent uses to
        supply a verifiable on-chain payment receipt.  A nonce-challenge
        mechanism prevents proof replay.  An optional verification endpoint
        allows third-party on-chain confirmation without requiring the verifying
        party to run blockchain infrastructure.
      </t>
      <t>
        FADP is designed for agent-to-server and agent-to-agent payment flows
        operating at sub-dollar granularity (micropayments), where credit-card
        or OAuth-based billing is impractical.  The reference implementation
        targets USDC on Base (an Ethereum Layer 2), though the protocol is
        token- and chain-agnostic.
      </t>
    </abstract>

    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        This document is submitted as an independent submission to the IETF
        datatracker.  It does not represent the consensus of any IETF working
        group.  The author welcomes discussion on the
        <eref target="https://github.com/fluidbase9/fadp">GitHub repository</eref>
        and via the contact address above.
      </t>
    </note>
  </front>

  <middle>

    <!-- ============================================================ -->
    <section anchor="introduction">
      <name>Introduction</name>

      <t>
        Autonomous AI agents—software processes that browse the web, call APIs,
        and complete tasks on behalf of users without per-action human
        approval—are an emerging class of HTTP client.  Unlike human-operated
        browsers, these agents cannot authenticate themselves to paid services
        via credit-card billing flows, OAuth consent screens, or similar
        interactive mechanisms.  The practical result is that most commercial
        APIs are inaccessible to fully autonomous agents, forcing developers to
        hard-code credentials that cannot be scoped, limited, or revoked
        per-task.
      </t>
      <t>
        HTTP has carried a 402 (Payment Required) status code since RFC 7231
        <xref target="RFC7231"/>, but that specification explicitly reserved
        the code for future use and defined no standard payment handshake.
        Numerous proprietary schemes have since emerged, but none has achieved
        broad adoption, and none addresses the specific needs of machine-to-machine
        micropayments at the scale and frequency that autonomous agents require.
      </t>
      <t>
        Blockchain-based stablecoins—particularly USDC on high-throughput,
        low-cost Layer 2 networks—have made sub-cent on-chain transfers
        economically viable for the first time.  A transaction settling 0.001
        USDC (one-tenth of one cent) on Base costs roughly USD 0.0001 in gas,
        making per-request billing at very fine granularity practical.
      </t>
      <t>
        FADP defines a minimal, HTTP-native handshake that combines these two
        properties: the well-understood 402 status code as a payment prompt,
        and an on-chain transfer as the payment instrument.  The protocol
        requires no browser, no OAuth server, no API-key issuance flow, and no
        pre-existing billing relationship between the agent and the server.
        An agent that has a funded wallet and a Fluid agent key can pay for
        any FADP-gated resource it discovers, at runtime, without human
        intervention—provided the requested amount is within the agent's
        pre-configured spending limit.
      </t>

      <section anchor="goals">
        <name>Design Goals</name>
        <ul>
          <li>
            <strong>HTTP-native</strong>: the entire payment handshake is
            expressed in standard HTTP status codes and header fields.  No
            new transport layer is required.
          </li>
          <li>
            <strong>Stateless from the agent's perspective</strong>: the server
            supplies all information needed to pay in the 402 response; the
            agent needs no prior knowledge of the server's payment address or
            preferred token.
          </li>
          <li>
            <strong>Replay-safe</strong>: a nonce bound to each payment
            challenge ensures that a valid payment proof cannot be reused
            across requests or across sessions.
          </li>
          <li>
            <strong>Verifier-decoupled</strong>: servers can delegate on-chain
            verification to a third-party service, removing the need to run
            or maintain blockchain node infrastructure.
          </li>
          <li>
            <strong>Token- and chain-agnostic</strong>: while USDC on Base is
            the reference currency, FADP carries token and chain identifiers
            in every message and imposes no restriction on supported assets.
          </li>
          <li>
            <strong>Human-in-the-loop friendly</strong>: spending limits and
            out-of-band approval flows are outside protocol scope but are
            natural affordances of agent runtime environments built on FADP.
          </li>
        </ul>
      </section>

      <section anchor="non-goals">
        <name>Non-Goals</name>
        <ul>
          <li>
            FADP does not specify how agents acquire, store, or rotate their
            wallet credentials.
          </li>
          <li>
            FADP does not define a streaming or subscription billing model.
            Each request-response pair is an independent payment event.
          </li>
          <li>
            FADP does not mandate a specific blockchain or token.
          </li>
          <li>
            FADP does not replace existing API authentication schemes; it may
            operate alongside them.
          </li>
        </ul>
      </section>
    </section>

    <!-- ============================================================ -->
    <section anchor="terminology">
      <name>Terminology</name>

      <t>
        The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
        "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
        "OPTIONAL" in this document are to be interpreted as described in
        BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and
        only when, they appear in all capitals, as shown here.
      </t>

      <dl>
        <dt>Agent</dt>
        <dd>
          An autonomous software process that issues HTTP requests on behalf
          of a user or another automated system, without interactive human
          approval for each request.
        </dd>

        <dt>Server</dt>
        <dd>
          An HTTP origin server that gates one or more resources behind a FADP
          payment requirement.
        </dd>

        <dt>Payment Challenge</dt>
        <dd>
          A 402 HTTP response carrying an <tt>X-FADP-Required</tt> header
          that specifies the payment terms the agent must satisfy.
        </dd>

        <dt>Payment Proof</dt>
        <dd>
          A JSON object carried in the <tt>X-FADP-Proof</tt> header of a
          subsequent request, asserting that the agent has executed an on-chain
          transfer matching the payment challenge.
        </dd>

        <dt>Nonce</dt>
        <dd>
          A cryptographically random, single-use token embedded in a payment
          challenge.  The server MUST reject any proof whose nonce has already
          been consumed or has expired.
        </dd>

        <dt>Verification Service</dt>
        <dd>
          An HTTPS endpoint that accepts a payment proof and independently
          confirms the corresponding on-chain transaction.  May be operated by
          a third party.
        </dd>

        <dt>On-chain Transfer</dt>
        <dd>
          A transaction recorded in a distributed ledger (blockchain) that
          transfers a specified amount of a digital asset from one address to
          another.
        </dd>

        <dt>Transaction Hash (txHash)</dt>
        <dd>
          A unique identifier for an on-chain transfer, derived from the
          cryptographic hash of the transaction data.  Immutable once the
          transaction is finalized.
        </dd>
      </dl>
    </section>

    <!-- ============================================================ -->
    <section anchor="protocol-overview">
      <name>Protocol Overview</name>

      <t>
        A FADP interaction consists of three HTTP exchanges:
      </t>

      <ol>
        <li>
          <strong>Initial Request</strong>: The agent sends an ordinary HTTP
          request to a protected resource, without any payment header.
        </li>
        <li>
          <strong>Payment Challenge</strong>: The server responds with
          HTTP 402 and an <tt>X-FADP-Required</tt> header describing the
          payment terms.  The agent executes an on-chain transfer matching
          those terms.
        </li>
        <li>
          <strong>Authenticated Request</strong>: The agent repeats the
          original request, adding an <tt>X-FADP-Proof</tt> header that
          references the completed on-chain transfer and the challenge nonce.
          The server verifies the proof and, if valid, fulfills the request.
        </li>
      </ol>

      <figure anchor="flow-diagram">
        <name>FADP Full Handshake</name>
        <artwork type="ascii-art"><![CDATA[
Agent                         Server                    Verifier
  |                              |                          |
  |  GET /resource               |                          |
  |----------------------------->|                          |
  |                              |                          |
  |  402 Payment Required        |                          |
  |  X-FADP-Required: {          |                          |
  |    version, amount, token,   |                          |
  |    chain, payTo, nonce,      |                          |
  |    expires, verifyUrl }      |                          |
  |<-----------------------------|                          |
  |                              |                          |
  |  [Agent executes on-chain transfer to payTo]            |
  |  [Receives txHash from blockchain]                      |
  |                              |                          |
  |  GET /resource               |                          |
  |  X-FADP-Proof: {             |                          |
  |    txHash, nonce, timestamp }|                          |
  |----------------------------->|                          |
  |                              |  POST verifyUrl          |
  |                              |  { txHash, payTo,        |
  |                              |    amount, token,        |
  |                              |    chain, nonce }        |
  |                              |------------------------->|
  |                              |                          |
  |                              |  { verified: true,       |
  |                              |    from, to, amount }    |
  |                              |<-------------------------|
  |                              |                          |
  |  200 OK + resource data      |                          |
  |<-----------------------------|                          |
        ]]></artwork>
      </figure>
    </section>

    <!-- ============================================================ -->
    <section anchor="header-fields">
      <name>Header Fields</name>

      <section anchor="x-fadp-required">
        <name>X-FADP-Required</name>

        <t>
          The <tt>X-FADP-Required</tt> response header field is sent by the
          server in a 402 response.  Its value is a JSON object with the
          following members:
        </t>

        <dl>
          <dt>version</dt>
          <dd>
            REQUIRED.  String.  MUST be <tt>"1.0"</tt> for this version of
            the protocol.
          </dd>

          <dt>amount</dt>
          <dd>
            REQUIRED.  String.  The amount of the specified token that the
            agent MUST transfer, expressed as a decimal number
            (e.g., <tt>"0.001"</tt>).  The server SHOULD use the minimal
            number of decimal places necessary to avoid ambiguity.
          </dd>

          <dt>token</dt>
          <dd>
            REQUIRED.  String.  The symbol of the digital asset to be
            transferred (e.g., <tt>"USDC"</tt>, <tt>"ETH"</tt>,
            <tt>"USDT"</tt>).  The symbol SHOULD conform to the canonical
            symbol registered with the issuer or a well-known token list.
          </dd>

          <dt>chain</dt>
          <dd>
            REQUIRED.  String.  The identifier of the blockchain network on
            which the transfer must be executed.  Registered values include
            <tt>"base"</tt> (Base Mainnet), <tt>"ethereum"</tt> (Ethereum
            Mainnet), and <tt>"solana"</tt> (Solana Mainnet).  Implementors
            MAY define additional chain identifiers using reverse-DNS
            notation to avoid collision.
          </dd>

          <dt>payTo</dt>
          <dd>
            REQUIRED.  String.  The blockchain address of the server operator's
            wallet.  For EVM-compatible chains, this MUST be a checksummed
            Ethereum address (EIP-55).
          </dd>

          <dt>nonce</dt>
          <dd>
            REQUIRED.  String.  A cryptographically random, single-use token
            generated by the server for this payment challenge.  The server
            MUST ensure that each nonce is unique across all active challenges.
            The nonce MUST be at least 16 bytes of entropy, encoded as a
            lowercase hexadecimal string (minimum 32 characters).
          </dd>

          <dt>expires</dt>
          <dd>
            REQUIRED.  Number.  A Unix timestamp (seconds since epoch)
            indicating when this payment challenge expires.  The server MUST
            reject proofs referencing a nonce whose expiry has passed.  The
            RECOMMENDED default TTL is 300 seconds (5 minutes).
          </dd>

          <dt>description</dt>
          <dd>
            OPTIONAL.  String.  A human-readable description of the resource
            or service being paid for (e.g., <tt>"Premium market data API"</tt>).
            Intended for display in agent UIs and transaction logs.
          </dd>

          <dt>verifyUrl</dt>
          <dd>
            OPTIONAL.  String.  An HTTPS URL identifying the verification
            service the server will use to confirm payment proofs.  If
            omitted, the default is
            <tt>https://fluidnative.com/v1/fadp/verify</tt>.  Servers
            operating their own verification infrastructure SHOULD set this
            field.
          </dd>
        </dl>

        <t>
          Example value (formatted for readability; actual header value MUST
          be a single-line JSON string):
        </t>

        <sourcecode type="json"><![CDATA[
{
  "version": "1.0",
  "amount": "0.001",
  "token": "USDC",
  "chain": "base",
  "payTo": "0xAbCd1234AbCd1234AbCd1234AbCd1234AbCd1234",
  "nonce": "a3f9c2b1d4e5f6a7b8c9d0e1f2a3b4c5",
  "expires": 1745001234,
  "description": "GPT-4o inference via OpenRouter proxy",
  "verifyUrl": "https://fluidnative.com/v1/fadp/verify"
}
        ]]></sourcecode>
      </section>

      <section anchor="x-fadp-proof">
        <name>X-FADP-Proof</name>

        <t>
          The <tt>X-FADP-Proof</tt> request header field is sent by the agent
          in the authenticated request.  Its value is a JSON object with the
          following members:
        </t>

        <dl>
          <dt>txHash</dt>
          <dd>
            REQUIRED.  String.  The transaction hash of the on-chain transfer
            executed by the agent in response to the payment challenge.  For
            EVM-compatible chains, this is a 0x-prefixed 32-byte hex string.
          </dd>

          <dt>nonce</dt>
          <dd>
            REQUIRED.  String.  The nonce value copied verbatim from the
            <tt>X-FADP-Required</tt> header of the payment challenge to which
            this proof responds.  The server MUST verify that this nonce
            matches a known, unexpired, unconsumed challenge.
          </dd>

          <dt>timestamp</dt>
          <dd>
            REQUIRED.  Number.  A Unix timestamp (seconds since epoch)
            representing the time at which the agent assembled this proof.
            The server SHOULD reject proofs where
            <tt>|currentTime - timestamp| &gt; 300</tt> seconds.
          </dd>

          <dt>agentKeyPrefix</dt>
          <dd>
            OPTIONAL.  String.  A non-sensitive prefix of the agent's
            credential (e.g., the first 8 characters of a <tt>fwag_</tt>
            key), used for logging and audit.  Implementations MUST NOT
            include the full credential.
          </dd>
        </dl>

        <t>Example:</t>

        <sourcecode type="json"><![CDATA[
{
  "txHash": "0xabc1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd",
  "nonce": "a3f9c2b1d4e5f6a7b8c9d0e1f2a3b4c5",
  "timestamp": 1745001200,
  "agentKeyPrefix": "fwag_a3f9"
}
        ]]></sourcecode>
      </section>
    </section>

    <!-- ============================================================ -->
    <section anchor="verification-endpoint">
      <name>Verification Endpoint</name>

      <t>
        The verification endpoint is an HTTPS resource that the server
        (or its delegate) calls to confirm that a payment proof corresponds
        to a valid on-chain transfer.  This section defines the request
        and response formats for that endpoint.
      </t>

      <section anchor="verify-request">
        <name>Verification Request</name>

        <t>
          The server sends an HTTP POST to the <tt>verifyUrl</tt> with a
          JSON body containing:
        </t>

        <dl>
          <dt>txHash</dt>
          <dd>REQUIRED.  String.  The transaction hash from the agent's proof.</dd>

          <dt>payTo</dt>
          <dd>REQUIRED.  String.  The wallet address that should have received the transfer.</dd>

          <dt>amount</dt>
          <dd>REQUIRED.  String.  The amount that should have been transferred.</dd>

          <dt>token</dt>
          <dd>REQUIRED.  String.  The token symbol.</dd>

          <dt>chain</dt>
          <dd>REQUIRED.  String.  The chain identifier.</dd>

          <dt>nonce</dt>
          <dd>REQUIRED.  String.  The nonce from the original challenge.</dd>
        </dl>
      </section>

      <section anchor="verify-response">
        <name>Verification Response</name>

        <t>
          The verification service responds with a JSON object:
        </t>

        <dl>
          <dt>verified</dt>
          <dd>
            <t>REQUIRED.  Boolean.  <tt>true</tt> if and only if all of the
            following conditions hold:</t>
            <ul>
              <li>The transaction identified by <tt>txHash</tt> exists on the specified chain.</li>
              <li>The transaction status is finalized (not reverted).</li>
              <li>The recipient address matches <tt>payTo</tt>.</li>
              <li>The transferred amount is greater than or equal to the required <tt>amount</tt>.</li>
              <li>The transferred token matches the required <tt>token</tt>.</li>
            </ul>
          </dd>

          <dt>txHash</dt>
          <dd>OPTIONAL.  String.  Echo of the verified transaction hash.</dd>

          <dt>amount</dt>
          <dd>OPTIONAL.  String.  The actual transferred amount.</dd>

          <dt>token</dt>
          <dd>OPTIONAL.  String.  The actual transferred token symbol.</dd>

          <dt>chain</dt>
          <dd>OPTIONAL.  String.  The chain on which the transfer occurred.</dd>

          <dt>from</dt>
          <dd>OPTIONAL.  String.  The sender's wallet address.</dd>

          <dt>to</dt>
          <dd>OPTIONAL.  String.  The recipient's wallet address (SHOULD match <tt>payTo</tt>).</dd>

          <dt>error</dt>
          <dd>OPTIONAL.  String.  A human-readable error message when <tt>verified</tt> is <tt>false</tt>.</dd>
        </dl>

        <t>Example successful response:</t>

        <sourcecode type="json"><![CDATA[
{
  "verified": true,
  "txHash": "0xabc123...",
  "amount": "0.001",
  "token": "USDC",
  "chain": "base",
  "from": "0xAgentWallet...",
  "to": "0xServerWallet..."
}
        ]]></sourcecode>

        <t>Example failed response:</t>

        <sourcecode type="json"><![CDATA[
{
  "verified": false,
  "error": "Transfer amount 0.0005 USDC is less than required 0.001 USDC"
}
        ]]></sourcecode>
      </section>
    </section>

    <!-- ============================================================ -->
    <section anchor="server-requirements">
      <name>Server Behavior</name>

      <section anchor="issuing-challenges">
        <name>Issuing Payment Challenges</name>
        <t>
          When a server receives a request for a FADP-protected resource
          without an <tt>X-FADP-Proof</tt> header, it MUST:
        </t>
        <ol>
          <li>Generate a nonce of at least 16 bytes of cryptographically secure random data.</li>
          <li>Record the nonce and its expiry time in durable server-side storage (memory, cache, or database).</li>
          <li>Respond with HTTP 402.</li>
          <li>Set the <tt>X-FADP-Required</tt> header to a JSON object as defined in <xref target="x-fadp-required"/>.</li>
          <li>Set <tt>Access-Control-Expose-Headers: X-FADP-Required</tt> to ensure cross-origin agents can read the header.</li>
        </ol>
      </section>

      <section anchor="verifying-proofs">
        <name>Verifying Payment Proofs</name>
        <t>
          When a server receives a request with an <tt>X-FADP-Proof</tt> header,
          it MUST perform the following checks in order, returning an appropriate
          error response if any check fails:
        </t>
        <ol>
          <li>Parse the header value as JSON; return 400 if malformed.</li>
          <li>Verify that <tt>txHash</tt>, <tt>nonce</tt>, and <tt>timestamp</tt> are present; return 400 if any is absent.</li>
          <li>Look up the nonce in server-side storage; return 402 if the nonce is unknown.</li>
          <li>Verify the nonce has not expired; return 402 if expired (delete the nonce).</li>
          <li>Verify that <tt>|currentTime - proof.timestamp| &lt;= 300</tt> seconds; return 402 if outside range.</li>
          <li>Call the verification service at <tt>verifyUrl</tt>; return 402 if <tt>verified</tt> is <tt>false</tt>.</li>
          <li>Atomically mark the nonce as consumed (delete it from storage) to prevent replay.</li>
          <li>Proceed to handle the original request.</li>
        </ol>
        <t>
          Step 7 (nonce consumption) MUST occur before the server fulfills the
          request.  If the server cannot atomically consume the nonce and fulfill
          the request (e.g., due to a crash), it SHOULD err on the side of not
          consuming the nonce, accepting the risk of a duplicate fulfillment
          rather than silently accepting payment without delivering service.
        </t>
      </section>
    </section>

    <!-- ============================================================ -->
    <section anchor="agent-requirements">
      <name>Agent Behavior</name>

      <t>
        An agent implementation SHOULD:
      </t>
      <ol>
        <li>
          On receiving a 402 with <tt>X-FADP-Required</tt>, validate that the
          requested amount does not exceed the agent's configured spending limit
          before proceeding.
        </li>
        <li>
          Execute the on-chain transfer to the address and chain specified in the
          payment challenge, transferring at least the specified <tt>amount</tt>
          of the specified <tt>token</tt>.
        </li>
        <li>
          Await sufficient blockchain confirmation before constructing the proof.
          The definition of "sufficient" is implementation-specific; at minimum,
          the transaction MUST appear in a block (status: success) before the
          proof is submitted.
        </li>
        <li>
          Construct an <tt>X-FADP-Proof</tt> object containing the
          <tt>txHash</tt>, the <tt>nonce</tt> from the challenge, and the current
          Unix timestamp.
        </li>
        <li>
          Retry the original request with the <tt>X-FADP-Proof</tt> header added.
        </li>
        <li>
          If the server returns another 402 with a new nonce (e.g., because the
          previous nonce expired), restart the payment flow.
        </li>
      </ol>
      <t>
        An agent MUST NOT reuse a <tt>txHash</tt> or <tt>nonce</tt> across
        separate request/response cycles.  Each resource access requires a
        fresh payment and a fresh proof.
      </t>
    </section>

    <!-- ============================================================ -->
    <section anchor="error-codes">
      <name>Error Responses</name>

      <t>
        FADP servers SHOULD include a JSON body in all error responses with at
        least the following fields:
      </t>

      <dl>
        <dt>error</dt>
        <dd>String.  A machine-readable error key.</dd>

        <dt>protocol</dt>
        <dd>String.  SHOULD be <tt>"FADP/1.0"</tt> to identify the protocol version.</dd>

        <dt>detail</dt>
        <dd>String.  OPTIONAL human-readable elaboration.</dd>
      </dl>

      <table>
        <name>FADP Error Conditions</name>
        <thead>
          <tr><th>HTTP Status</th><th>Condition</th><th>Recommended <tt>error</tt> value</th></tr>
        </thead>
        <tbody>
          <tr><td>400</td><td>Malformed <tt>X-FADP-Proof</tt> header (not valid JSON)</td><td><tt>invalid_proof_format</tt></td></tr>
          <tr><td>400</td><td>Missing required fields in proof</td><td><tt>missing_proof_fields</tt></td></tr>
          <tr><td>402</td><td>No proof header present (initial challenge)</td><td><tt>payment_required</tt></td></tr>
          <tr><td>402</td><td>Nonce unknown (never issued or already expired from store)</td><td><tt>unknown_nonce</tt></td></tr>
          <tr><td>402</td><td>Nonce expired (TTL elapsed)</td><td><tt>nonce_expired</tt></td></tr>
          <tr><td>402</td><td>Proof timestamp too old or too far in future</td><td><tt>proof_timestamp_invalid</tt></td></tr>
          <tr><td>402</td><td>On-chain verification returned <tt>verified: false</tt></td><td><tt>payment_verification_failed</tt></td></tr>
          <tr><td>402</td><td>Transferred amount less than required</td><td><tt>insufficient_payment</tt></td></tr>
          <tr><td>403</td><td>Nonce has already been consumed (replay attempt)</td><td><tt>nonce_already_used</tt></td></tr>
        </tbody>
      </table>
    </section>

    <!-- ============================================================ -->
    <section anchor="chain-identifiers">
      <name>Chain Identifiers</name>

      <t>
        FADP uses short string identifiers for blockchain networks.  The
        following values are defined by this specification:
      </t>

      <table>
        <name>FADP Chain Identifier Registry</name>
        <thead>
          <tr><th>Identifier</th><th>Network</th><th>Native Currency</th><th>EVM Chain ID</th></tr>
        </thead>
        <tbody>
          <tr><td><tt>base</tt></td><td>Base Mainnet (Coinbase L2)</td><td>ETH</td><td>8453</td></tr>
          <tr><td><tt>ethereum</tt></td><td>Ethereum Mainnet</td><td>ETH</td><td>1</td></tr>
          <tr><td><tt>solana</tt></td><td>Solana Mainnet Beta</td><td>SOL</td><td>N/A</td></tr>
          <tr><td><tt>injective</tt></td><td>Injective Mainnet</td><td>INJ</td><td>N/A</td></tr>
          <tr><td><tt>base-sepolia</tt></td><td>Base Sepolia Testnet</td><td>ETH</td><td>84532</td></tr>
        </tbody>
      </table>

      <t>
        Implementors that wish to support chains not listed here SHOULD use
        identifiers of the form <tt>{namespace}-{networkname}</tt> to avoid
        collisions with future registered values.
      </t>
    </section>

    <!-- ============================================================ -->
    <section anchor="security-considerations">
      <name>Security Considerations</name>

      <section anchor="sec-replay">
        <name>Replay Attacks</name>
        <t>
          The nonce mechanism is the primary defense against replay attacks.
          A valid <tt>txHash</tt> from a past payment cannot be reused
          because:
        </t>
        <ol>
          <li>Each payment challenge carries a unique nonce.</li>
          <li>The proof MUST echo that nonce.</li>
          <li>The server atomically consumes the nonce upon first successful verification.</li>
          <li>Subsequent proofs referencing the same nonce are rejected with 403.</li>
        </ol>
        <t>
          Server implementations MUST use cryptographically secure random number
          generation for nonces (e.g., <tt>/dev/urandom</tt> or equivalent).
          Predictable nonces would allow an attacker to pre-compute valid proofs.
        </t>
        <t>
          The nonce store MUST be shared across all instances of a horizontally
          scaled server deployment.  If a local per-process nonce store is used,
          the same nonce could be accepted by two different instances simultaneously,
          creating a replay window.  A distributed cache (e.g., Redis) or a
          database with atomic compare-and-delete operations is RECOMMENDED for
          production deployments.
        </t>
      </section>

      <section anchor="sec-overpay">
        <name>Overpayment</name>
        <t>
          The verification service checks that the transferred amount is
          <em>at least</em> the required amount.  Servers SHOULD NOT issue
          refunds for overpayments, as the complexity of doing so on-chain
          outweighs the benefit for small amounts.  Agents SHOULD transfer
          exactly the required amount to avoid unnecessary token loss.
        </t>
      </section>

      <section anchor="sec-front-running">
        <name>Front-Running and Transaction Interception</name>
        <t>
          In principle, an attacker observing the agent's on-chain transaction
          in the mempool could copy the <tt>txHash</tt> and attempt to submit
          a proof before the legitimate agent.  This attack is mitigated by:
        </t>
        <ol>
          <li>
            The nonce binding: the attacker must have obtained the nonce from
            a legitimate 402 challenge targeted at their own session.  Nonces
            are per-session and cannot be forged.
          </li>
          <li>
            The proof timestamp check: the server rejects proofs assembled more
            than 300 seconds after the challenge was issued.
          </li>
        </ol>
        <t>
          Servers that require strong session binding MAY add an additional
          check verifying that the HTTP session initiating the proof matches
          the session that triggered the original challenge (e.g., via a
          session cookie or TLS channel binding), though this is outside the
          scope of the current protocol version.
        </t>
      </section>

      <section anchor="sec-verifier-trust">
        <name>Verification Service Trust</name>
        <t>
          Servers that delegate verification to a third-party service are
          implicitly trusting that service's honesty and availability.  A
          compromised or malicious verification service could return
          <tt>verified: true</tt> for invalid proofs, allowing unauthorized
          access.
        </t>
        <t>
          Servers operating in high-value contexts SHOULD run their own
          verification infrastructure against a direct blockchain RPC
          endpoint.  The verification endpoint API defined in
          <xref target="verification-endpoint"/> is designed to be
          self-hostable for this reason.
        </t>
      </section>

      <section anchor="sec-blockchain-finality">
        <name>Blockchain Finality</name>
        <t>
          On chains with probabilistic finality (including Ethereum and Base),
          a transaction that appears in a block may be reorged out of the
          canonical chain.  Servers accepting payments for high-value resources
          SHOULD wait for a chain-specific number of confirmation blocks before
          accepting a proof as final.  For low-value micropayments on Base (an
          optimistic rollup with fast soft-finality), a single block
          confirmation is typically sufficient.
        </t>
        <t>
          Verification services MUST check that the transaction receipt status
          indicates success (i.e., it was not reverted), not merely that a
          transaction with the given hash exists.
        </t>
      </section>

      <section anchor="sec-dos">
        <name>Nonce Store Exhaustion</name>
        <t>
          An adversary could generate large numbers of initial requests to
          force the server to allocate nonces, potentially exhausting memory
          if the nonce store grows without bound.  Server implementations
          MUST periodically prune expired nonces and SHOULD rate-limit the
          issuance of payment challenges per IP address or session.
        </t>
      </section>

      <section anchor="sec-header-injection">
        <name>Header Injection</name>
        <t>
          The <tt>X-FADP-Required</tt> and <tt>X-FADP-Proof</tt> header values
          are JSON strings.  Implementations MUST parse these with a
          conformant JSON parser and MUST NOT construct them via string
          interpolation without proper escaping, to prevent injection attacks.
        </t>
      </section>

      <section anchor="sec-tls">
        <name>Transport Security</name>
        <t>
          All FADP exchanges, including calls to the verification service,
          MUST be made over TLS (HTTPS).  Transmitting payment challenges or
          proofs over plaintext HTTP exposes the nonce to network adversaries,
          potentially enabling race-condition replay attacks.
        </t>
      </section>

      <section anchor="sec-spending-limits">
        <name>Agent Spending Controls</name>
        <t>
          FADP does not define agent-side spending controls; these are the
          responsibility of the agent runtime.  However, agent implementors
          MUST validate the requested <tt>amount</tt> in the payment challenge
          against a pre-configured limit before executing any transfer.
          Failing to do so could allow a malicious server to drain an agent's
          wallet by issuing artificially large payment challenges.
        </t>
      </section>
    </section>

    <!-- ============================================================ -->
    <section anchor="iana-considerations">
      <name>IANA Considerations</name>

      <section anchor="iana-headers">
        <name>HTTP Header Field Registration</name>
        <t>
          This document requests registration of the following header fields in
          the "Hypertext Transfer Protocol (HTTP) Field Name Registry"
          maintained at
          <eref target="https://www.iana.org/assignments/http-fields/"/>.
        </t>

        <dl>
          <dt>Header Field Name</dt>
          <dd><tt>X-FADP-Required</tt></dd>
          <dt>Status</dt>
          <dd>provisional</dd>
          <dt>Reference</dt>
          <dd>This document, <xref target="x-fadp-required"/></dd>
        </dl>

        <dl>
          <dt>Header Field Name</dt>
          <dd><tt>X-FADP-Proof</tt></dd>
          <dt>Status</dt>
          <dd>provisional</dd>
          <dt>Reference</dt>
          <dd>This document, <xref target="x-fadp-proof"/></dd>
        </dl>
      </section>

      <section anchor="iana-media-type">
        <name>Media Type</name>
        <t>
          No new media types are registered by this document.  All message
          bodies are <tt>application/json</tt> as defined in
          <xref target="RFC8259"/>.
        </t>
      </section>
    </section>

    <!-- ============================================================ -->
    <section anchor="implementation-notes">
      <name>Implementation Notes</name>

      <section anchor="impl-reference">
        <name>Reference Implementations</name>
        <t>
          Reference implementations of the server middleware
          (<tt>fadpGate()</tt>) and client interceptor
          (<tt>fadpFetch()</tt>) are available as open-source software:
        </t>
        <ul>
          <li>
            <strong>fluid-fadp</strong> (npm): server middleware and standalone
            client interceptor for Node.js / TypeScript.
            Repository: <eref target="https://github.com/fluidbase9/fadp"/>
          </li>
          <li>
            <strong>fluid-wallet-agentkit</strong> (npm): full agent SDK
            incorporating FADP auto-pay via the <tt>agent.fetch()</tt> method.
            Repository: <eref target="https://github.com/fluidbase9/fluidwalletbase"/>
          </li>
          <li>
            <strong>fluid-fadp-proxy</strong> (npm): a ready-made reverse proxy
            that gates commercial AI service APIs (OpenRouter, RunPod, Vast.ai,
            Spheron) behind FADP payment walls.
            Repository: <eref target="https://github.com/fluidbase9/fadp-proxy"/>
          </li>
        </ul>
      </section>

      <section anchor="impl-nonce-storage">
        <name>Nonce Storage in Distributed Deployments</name>
        <t>
          For single-process servers, an in-memory <tt>Map</tt> with periodic
          TTL pruning is sufficient.  For multi-instance deployments, a shared
          store with atomic operations is required.  A Redis
          <tt>SET NX EX</tt> command provides an atomic "set if not exists with
          expiry" primitive suitable for nonce issuance; a Redis <tt>DEL</tt>
          returning the count of deleted keys provides atomic consumption.
        </t>
      </section>

      <section anchor="impl-amount-precision">
        <name>Amount Precision</name>
        <t>
          Token amounts MUST be represented as decimal strings to avoid
          floating-point precision loss.  On-chain, USDC uses 6 decimal places;
          the string <tt>"0.001"</tt> corresponds to on-chain value
          <tt>1000</tt> (in the smallest unit).  Implementations MUST convert
          between decimal string and on-chain integer representation using
          fixed-point arithmetic, not IEEE 754 floating-point.
        </t>
      </section>

      <section anchor="impl-cors">
        <name>Cross-Origin Resource Sharing</name>
        <t>
          Browser-based agent implementations require that
          <tt>X-FADP-Required</tt> be listed in the
          <tt>Access-Control-Expose-Headers</tt> response header for the
          402 response.  Servers MUST set this header when cross-origin
          clients are expected.
        </t>
      </section>
    </section>

  </middle>

  <back>

    <references>
      <name>References</name>

      <references>
        <name>Normative References</name>

        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author initials="S." surname="Bradner" fullname="Scott Bradner"/>
            <date year="1997" month="March"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>

        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author initials="B." surname="Leiba" fullname="Barry Leiba"/>
            <date year="2017" month="May"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>

        <reference anchor="RFC7231">
          <front>
            <title>Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content</title>
            <author initials="R." surname="Fielding" fullname="Roy T. Fielding"/>
            <author initials="J." surname="Reschke" fullname="Julian F. Reschke"/>
            <date year="2014" month="June"/>
          </front>
          <seriesInfo name="RFC" value="7231"/>
          <seriesInfo name="DOI" value="10.17487/RFC7231"/>
        </reference>

        <reference anchor="RFC9110">
          <front>
            <title>HTTP Semantics</title>
            <author initials="R." surname="Fielding" fullname="Roy T. Fielding"/>
            <author initials="M." surname="Nottingham" fullname="Mark Nottingham"/>
            <author initials="J." surname="Reschke" fullname="Julian F. Reschke"/>
            <date year="2022" month="June"/>
          </front>
          <seriesInfo name="RFC" value="9110"/>
          <seriesInfo name="DOI" value="10.17487/RFC9110"/>
        </reference>

        <reference anchor="RFC8259">
          <front>
            <title>The JavaScript Object Notation (JSON) Data Interchange Format</title>
            <author initials="T." surname="Bray" fullname="Tim Bray"/>
            <date year="2017" month="December"/>
          </front>
          <seriesInfo name="RFC" value="8259"/>
          <seriesInfo name="DOI" value="10.17487/RFC8259"/>
        </reference>

      </references>

      <references>
        <name>Informative References</name>

        <reference anchor="EIP-20">
          <front>
            <title>ERC-20 Token Standard</title>
            <author initials="F." surname="Vogelsteller" fullname="Fabian Vogelsteller"/>
            <author initials="V." surname="Buterin" fullname="Vitalik Buterin"/>
            <date year="2015" month="November"/>
          </front>
          <refcontent>Ethereum Improvement Proposal 20</refcontent>
          <annotation>Defines the fungible token interface implemented by USDC and most payment tokens used with FADP.</annotation>
        </reference>

        <reference anchor="EIP-55">
          <front>
            <title>Mixed-case checksum address encoding</title>
            <author initials="V." surname="Buterin" fullname="Vitalik Buterin"/>
            <date year="2016" month="January"/>
          </front>
          <refcontent>Ethereum Improvement Proposal 55</refcontent>
          <annotation>Defines the checksummed Ethereum address format used in FADP address fields.</annotation>
        </reference>

        <reference anchor="BASE-L2">
          <front>
            <title>Base: An Ethereum L2 for the Next Billion Users</title>
            <author><organization>Coinbase</organization></author>
            <date year="2023"/>
          </front>
          <refcontent>Technical overview, base.org</refcontent>
          <annotation>Base Mainnet is the reference deployment chain for FADP's reference implementation.</annotation>
        </reference>

        <reference anchor="x402">
          <front>
            <title>x402: An Open HTTP Payment Protocol</title>
            <author><organization>Coinbase Developer Platform</organization></author>
            <date year="2025"/>
          </front>
          <refcontent>github.com/coinbase/x402</refcontent>
          <annotation>A contemporaneous HTTP 402-based payment protocol from Coinbase. FADP shares the 402 status code and on-chain settlement approach but differs in nonce mechanism, header naming, and protocol extensibility design.</annotation>
        </reference>

      </references>
    </references>

    <section anchor="full-example" numbered="false">
      <name>Appendix A: Complete Exchange Example</name>

      <t>The following illustrates a complete FADP exchange over HTTP/1.1.</t>

      <t><strong>Step 1 — Initial Request (no proof):</strong></t>
      <sourcecode type="http"><![CDATA[
POST /v1/chat/completions HTTP/1.1
Host: proxy.example.com
Content-Type: application/json

{"model":"openai/gpt-4o","messages":[{"role":"user","content":"Hello"}]}
      ]]></sourcecode>

      <t><strong>Step 2 — Payment Challenge:</strong></t>
      <sourcecode type="http"><![CDATA[
HTTP/1.1 402 Payment Required
Content-Type: application/json
X-FADP-Required: {"version":"1.0","amount":"0.001","token":"USDC",
  "chain":"base","payTo":"0xAbCd1234AbCd1234AbCd1234AbCd1234AbCd1234",
  "nonce":"a3f9c2b1d4e5f6a7b8c9d0e1f2a3b4c5","expires":1745001534,
  "description":"GPT-4o inference","verifyUrl":"https://fluidnative.com/v1/fadp/verify"}
Access-Control-Expose-Headers: X-FADP-Required

{"error":"Payment required","protocol":"FADP/1.0"}
      ]]></sourcecode>

      <t><strong>Step 3 — Agent executes on-chain USDC transfer on Base, receives txHash.</strong></t>

      <t><strong>Step 4 — Authenticated Request:</strong></t>
      <sourcecode type="http"><![CDATA[
POST /v1/chat/completions HTTP/1.1
Host: proxy.example.com
Content-Type: application/json
X-FADP-Proof: {"txHash":"0xabc123...","nonce":"a3f9c2b1d4e5f6a7b8c9d0e1f2a3b4c5",
  "timestamp":1745001480}

{"model":"openai/gpt-4o","messages":[{"role":"user","content":"Hello"}]}
      ]]></sourcecode>

      <t><strong>Step 5 — Successful Response (after on-chain verification):</strong></t>
      <sourcecode type="http"><![CDATA[
HTTP/1.1 200 OK
Content-Type: application/json

{"choices":[{"message":{"role":"assistant","content":"Hello! How can I help?"}}]}
      ]]></sourcecode>
    </section>

    <section anchor="acknowledgements" numbered="false">
      <name>Acknowledgements</name>
      <t>
        The author thanks the teams building open agent infrastructure across
        the Ethereum, Base, and Solana ecosystems whose work made practical
        micropayment settlement possible.  The design of FADP was informed by
        the x402 protocol from Coinbase Developer Platform, the Model Context
        Protocol (MCP), and early feedback from developers building autonomous
        agents on the Fluid Wallet platform.
      </t>
    </section>

  </back>
</rfc>
