> ## Documentation Index
> Fetch the complete documentation index at: https://docs.atxp.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch Payments

> Learn how to use batch payments to pay for MCP server tool calls

## Overview

Batch payments let your users pre-pay a larger amount once and draw down across multiple tool calls. You configure a minimum upfront charge and continue to require a per-call price; the middleware handles balance tracking so users are not prompted on every call.

## When to use batch payments

* If your tool is called repeatedly in a workflow and per-call prompts are disruptive
* If you want to reduce payment-approval friction while keeping per-call pricing
* If you want to amortize network fees over several calls

<Tip>
  Pick a minimum payment that covers several calls (e.g., 10× your tool's per-call price) to reduce prompts without overcharging.
</Tip>

## How it works

1. You set `minimumPayment` in the `atxpExpress` middleware.
2. Each tool still calls `requirePayment({ price })` for its per-call charge.
3. On the first call, the user pays the larger of `minimumPayment` and `price`.
4. Subsequent calls deduct `price` from the remaining balance until depleted.

## Prerequisites

* ATXP account and connection string
* Existing Express-based MCP server using the [ATXP Express SDK](/developers/api-reference/express).

<Steps>
  <Step title="Install dependencies">
    ```bash theme={null}
    npm install @atxp/express bignumber.js
    ```
  </Step>

  <Step title="Configure server with minimumPayment">
    Add `minimumPayment` to your ATXP middleware and keep per-call pricing with `requirePayment`.

    ```typescript server.ts theme={null}
    import express from 'express'
    import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
    import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
    import { atxpExpress, requirePayment, ATXPAccount } from '@atxp/express'
    import BigNumber from 'bignumber.js'
    import { z } from 'zod'

    const app = express()
    app.use(express.json())

    const ATXP_CONNECTION = process.env.ATXP_CONNECTION

    // Require an upfront prepayment of $0.50 USDC (or higher if per-call price is larger)
    app.use(
      atxpExpress({
        destination: new ATXPAccount(ATXP_CONNECTION),
        payeeName: 'My Batch-Paid Tool',
        minimumPayment: new BigNumber(0.50),
      })
    )

    const server = new McpServer({ name: 'my-batch-server', version: '1.0.0' })
    const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined })

    // Charge $0.05 USDC per call; draws down from the prepaid balance
    server.tool(
      'process_text',
      'Process a text string with batch-paid pricing',
      { text: z.string().describe('Text to process') },
      async ({ text }) => {
        await requirePayment({ price: new BigNumber(0.05) })
        return {
          content: [{ type: 'text', text: text.toUpperCase() }],
        }
      }
    )

    const setupServer = async () => {
      await server.connect(transport)
    }

    app.post('/', async (req, res) => {
      try {
        await transport.handleRequest(req, res, req.body)
      } catch (err) {
        if (!res.headersSent) res.status(500).json({ error: 'Internal server error' })
      }
    })

    const PORT = process.env.PORT || 3000
    setupServer().then(() => app.listen(PORT))
    ```

    <Warning>
      Ensure `minimumPayment` is a `BigNumber` instance. If you pass a number, amounts may be imprecise.
    </Warning>
  </Step>

  <Step title="Client behavior and approval">
    The first tool call will request the larger amount. You can implement approval logic to auto‑approve reasonable prepayments.

    ```typescript client.ts theme={null}
    import { atxpClient } from '@atxp/client'
    import { Account } from '@atxp/common'

    const account = new Account(process.env.ATXP_CONNECTION_STRING!)
    const client = await atxpClient({
      mcpServer: 'https://your-server.example.com',
      account,
      onPayment: async (pmt) => {
        console.log(`Payment succeeded: ${pmt.amount} ${pmt.currency}`)
      },
      onPaymentFailure: async (err) => {
        console.error('Payment failed', err)
      },
    })

    const result = await client.callTool({
      name: 'process_text',
      arguments: { text: 'hello world' },
    })
    ```

    <Tip>
      To persist balances across restarts or scale-out, configure an OAuth database in `atxpExpress` (SQLite or Redis).
    </Tip>
  </Step>

  <Step title="Verify batch behavior">
    * First call prompts for the larger `minimumPayment` (e.g., \$0.50)
    * Next several calls are not prompted until the prepaid balance is depleted
    * Once depleted, the next call will prompt again for at least `minimumPayment`

    <Check>
      You should see only a single payment approval across multiple calls, then another prompt once the balance runs out.
    </Check>
  </Step>
</Steps>
