Skip to main content
Cookbooks

Marketplace Payout

Split a marketplace transaction into platform fee + seller payout. One agent, two settled transactions, full audit trail.

1 min read · updated
View MarkdownEdit on GitHub
TIME
~15 min
PROVIDER
Claudeopus-4-6
SERVERS
stripestark-banknuvem-fiscal

A marketplace collects a charge from the buyer, keeps its platform fee, and pays the seller the remainder. Two settled transactions (one charge + one payout) — $0.20 total, everything else included. The agent handles the whole flow: capture, reconcile, payout, invoice.

The money flow

StepMeta-toolDirectionBilled
1. Charge buyer (R$1,000)codespar_pay (card via Stripe)Buyer → Platform1 tx ($0.10)
2. Calculate split (platform fee 15%)(logic, not billed)
3. Payout seller (R$850 via Pix)codespar_pay (payout via Stark Bank)Platform → Seller1 tx ($0.10)
4. Issue NF-e for platform fee (R$150)codespar_invoiceincluded in charge tx

Total: 2 transactions × $0.10 = $0.20, regardless of how many providers the flow touches.

Prerequisites

npm install @codespar/sdk @codespar/claude @anthropic-ai/sdk

You need the merchant's own Stripe account + a Stark Bank account with outbound Pix enabled. See Authentication > Connect Links for user-level OAuth.

Full agent code

marketplace-payout.ts
import Anthropic from "@anthropic-ai/sdk";
import { CodeSpar } from "@codespar/sdk";
import { getTools, handleToolUse, toToolResultBlock } from "@codespar/claude";

const claude = new Anthropic();
const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });

interface Order {
  order_id: string;
  buyer: { email: string; card_token: string };
  seller: { id: string; bank_pix_key: string; cnpj: string };
  gross_amount: number; // centavos, e.g. 100000 = R$1,000
  platform_fee_bps: number; // basis points, e.g. 1500 = 15%
}

async function marketplacePayout(order: Order) {
  const session = await codespar.create(order.seller.id, {
    servers: ["stripe", "stark-bank", "nuvem-fiscal"],
    metadata: {
      order_id: order.order_id,
      marketplace_flow: "payout_v1",
    },
  });

  try {
    const tools = await getTools(session);
    const platformFee = Math.round(order.gross_amount * order.platform_fee_bps / 10000);
    const payoutAmount = order.gross_amount - platformFee;

    const messages: Anthropic.MessageParam[] = [
      {
        role: "user",
        content: `
Execute a marketplace order settlement:

1. Charge R$${(order.gross_amount / 100).toFixed(2)} on the buyer card (token: ${order.buyer.card_token}) via Stripe.
2. Once the charge settles, pay out R$${(payoutAmount / 100).toFixed(2)} via Pix to the seller key ${order.seller.bank_pix_key} via Stark Bank.
3. Issue an NF-e for the platform fee R$${(platformFee / 100).toFixed(2)} to seller CNPJ ${order.seller.cnpj} via Nuvem Fiscal.
4. Return the charge_id, payout_id, and nfe_access_key.

Stop immediately if the charge fails — do not attempt payout or invoice.
        `,
      },
    ];

    let response = await claude.messages.create({
      model: "claude-opus-4-6",
      max_tokens: 4096,
      tools,
      messages,
    });

    let iterations = 0;
    while (response.stop_reason === "tool_use" && iterations < 10) {
      const blocks = response.content.filter(
        (b): b is Anthropic.ToolUseBlock => b.type === "tool_use",
      );
      const results = await Promise.all(
        blocks.map(async (block) => {
          const out = await handleToolUse(session, block);
          return toToolResultBlock(block.id, out);
        }),
      );
      messages.push({ role: "assistant", content: response.content });
      messages.push({ role: "user", content: results });
      response = await claude.messages.create({
        model: "claude-opus-4-6",
        max_tokens: 4096,
        tools,
        messages,
      });
      iterations++;
    }

    const final = response.content.find(
      (b): b is Anthropic.TextBlock => b.type === "text",
    );
    return final?.text ?? "";
  } finally {
    await session.close();
  }
}

Deterministic mode

If the flow never branches and you do not need LLM reasoning mid-run, swap the Claude loop for session.loop(). Faster, cheaper, easier to debug:

const result = await session.loop({
  steps: [
    {
      tool: "codespar_pay",
      params: { method: "card", amount: order.gross_amount, card_token: order.buyer.card_token },
    },
    {
      tool: "codespar_pay",
      params: (prev) => ({
        action: "payout",
        method: "pix",
        amount: order.gross_amount - platformFee,
        pix_key: order.seller.bank_pix_key,
        source_payment_id: prev[0].data.payment_id,
      }),
    },
    {
      tool: "codespar_invoice",
      params: (prev) => ({
        type: "nfe",
        amount: platformFee,
        customer: { cnpj: order.seller.cnpj },
        reference: prev[0].data.payment_id,
      }),
    },
  ],
  abortOnError: true, // stop if the charge fails
});

Variations

Escrow / milestone release

Hold the charge in an escrow account until the seller fulfills the order. Pay out only after a shipping-confirmed event. Use the managed escrow feature on the Orchestration tier — your agent calls a single codespar_pay with escrow: true and releases it with a follow-up call after the fulfillment webhook.

Split across multiple sellers

For a multi-vendor cart (N sellers per order), turn the payout step into a loop:

steps: [
  { tool: "codespar_pay", params: { method: "card", amount: order.gross } },
  ...order.sellers.map((seller) => ({
    tool: "codespar_pay",
    params: { action: "payout", method: "pix", amount: seller.share, pix_key: seller.pix },
  })),
]

Each payout is one settled transaction.

Next steps

Last updated on