Invoices · AP Automation · RLUSD

Invoice verification & payment

Work is already done. Lock payment in XRPL escrow as a confirmed payment intent — the supplier submits a matching invoice and receives payment automatically. No manual AP approval, no payment chasing, no trusted intermediary. Works for humans, agents, or any mix of both.

The coffee delivery scenario

What happens, step by step

A business hires an individual to deliver coffee to a warehouse. Terms are agreed over email: supplier name, XRPL wallet address, amount, description.

A week later, the warehouse confirms receipt. The finance team creates an escrow — funds are locked on-chain as confirmed payment intent. The supplier receives an email: "Your payment of $500 RLUSD is ready to claim at our supplier portal. Use PO reference PO-2024-0042."

The supplier visits the portal, fills in their invoice details (or uploads a PDF), and submits. The AI referee checks whether the invoice matches the PO — supplier name, amount, description. If it matches: payment hits their wallet within seconds. If something's wrong: they see exactly what to correct and resubmit.

The finance team's accounts inbox receives a copy of the verified invoice automatically. No manual approval step. No chasing.

Key insight: the escrow isn't a payment gate against future work — it's a confirmed payment intent for work already done. The supplier sees locked funds before submitting anything. That's the difference from a normal invoice chase.

Any combination of human and agent

Only one side needs to be "smart" for the system to work. A business can deploy this unilaterally — every supplier benefits without changing their behaviour much.

Human buyer · Human supplier

The most common case today

Finance team raises a PO manually and locks escrow via the portal. Supplier visits the supplier portal, fills in their invoice, clicks submit. Both sides use a web form — no code required on either side.

Human buyer · Agent supplier

Supplier automates their billing

Finance team locks escrow as normal. The supplier is a software company or freelancer whose billing system detects the escrow via webhook and automatically generates a conforming invoice and calls /evaluate. The supplier gets paid without any human action on their side.

Agent buyer · Human supplier

Buyer automates their AP trigger

A warehouse management system or ERP confirms delivery and automatically creates the escrow — no finance team involvement. The supplier (a small courier, an individual contractor) receives an email, visits the portal, and uploads their invoice manually. They need no technical knowledge.

Agent buyer · Agent supplier

Fully automated AP

Delivery confirmed → escrow auto-created → supplier agent detects it → invoice auto-generated and submitted → payment released. Zero human involvement in the payment cycle. Humans only see edge cases: mismatches, disputes, unusual amounts.

How it works

1
Buyer / Finance team / ERP

Work is agreed and completed offline

A contractor works for a week. Goods are delivered to a warehouse. Terms were agreed beforehand — amount, supplier, description — outside of AgentTrust, just as normal.

2
Buyer / Finance team / ERP

Lock payment in escrow — confirmed payment intent

The buyer creates an escrow specifying the supplier wallet, amount, and a description of what the invoice must contain. Includes an accounts email so verified invoices are forwarded to the right inbox automatically. The escrow ID is shared with the supplier (by email, portal link, or webhook).

3
Supplier — human or agent

Submit the invoice

Human: visits the supplier portal, fills in their invoice details. Agent: receives a webhook, generates the invoice programmatically, calls POST /evaluate directly. Either way — escrow ID, invoice number, company name, amount, description of services delivered.

4
Automatic

AI verifies, payment releases, invoice forwarded

PASS: payment released to supplier wallet instantly. Invoice PDF emailed to the buyer's accounts address. FAIL: supplier sees exactly what to correct and can resubmit (up to the configured limit).

Quick start or white-label integration

1

Use AgentTrust directly

Enable Invoice Mode on the main escrow form. Tick the checkbox, enter a PO reference, and the task spec auto-fills. Supplier claims payment via their own escrow ID.

Open escrow form
2

White-label into your own site

Embed the buyer and supplier forms directly into your supplier portal or ERP. Your brand, your domain, your workflow. AgentTrust handles the escrow and AI verification invisibly behind the scenes.

Embeddable forms for your supplier portal

Two HTML forms: one for the buyer (raises the PO and locks escrow), one for the supplier (submits their invoice to claim payment). Style them to match your brand.

Used by your finance team (or triggered automatically by your ERP) to lock payment. Posts to your own backend endpoint — your wallet secret never touches the browser. The accounts email field ensures a copy of every verified invoice lands in the right inbox.

<!-- AgentTrust Buyer Form — embed in your supplier portal -->
<style>
  .at-form { font-family: system-ui, sans-serif; max-width: 480px; }
  .at-form label { display: block; font-size: .85rem; font-weight: 600; margin-bottom: .3rem; }
  .at-form input, .at-form textarea, .at-form select {
    width: 100%; padding: .6rem .8rem; border: 1px solid #dde0ea;
    border-radius: 8px; font-size: .9rem; margin-bottom: 1rem; font-family: inherit;
  }
  .at-form button { padding: .65rem 1.5rem; background: #0066FF; color: #fff;
    border: none; border-radius: 8px; font-size: .9rem; font-weight: 700; cursor: pointer; }
  .at-status { margin-top: 1rem; font-size: .85rem; padding: .75rem 1rem;
    border-radius: 8px; display: none; }
  .at-status.success { background: #f0fdf8; border: 1px solid #bbf7d0; color: #166534; display: block; }
  .at-status.error   { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; display: block; }
</style>

<form class="at-form" onsubmit="atCreateEscrow(event)">
  <h3>Lock Payment for Supplier</h3>

  <label>Purchase Order Number</label>
  <input id="at-po" placeholder="PO-2024-0042" required>

  <label>Supplier Company Name</label>
  <input id="at-supplier-name" placeholder="Acme Logistics Ltd" required>

  <label>Supplier XRPL Wallet Address</label>
  <input id="at-supplier-wallet" placeholder="rXXX…" required>

  <label>Amount (RLUSD)</label>
  <input id="at-amount" type="number" min="1" step="0.01" placeholder="500.00" required>

  <label>Services / Goods Description</label>
  <textarea id="at-description" rows="3"
    placeholder="e.g. Coffee delivery to Warehouse B — 200kg, delivered week of 14 Aug" required></textarea>

  <label>Send verified invoices to (accounts email)</label>
  <input id="at-accounts-email" type="email" placeholder="accounts@yourcompany.com" required>

  <label>Payment Window</label>
  <select id="at-deadline">
    <option value="168">7 days</option>
    <option value="336">14 days</option>
    <option value="720" selected>30 days</option>
  </select>

  <button type="submit">Lock Payment in Escrow</button>
  <div class="at-status" id="at-buyer-status"></div>
</form>

<script>
async function atCreateEscrow(e) {
  e.preventDefault();
  const status = document.getElementById('at-buyer-status');
  status.className = 'at-status';
  status.textContent = 'Creating escrow…';
  status.style.display = 'block';

  const po          = document.getElementById('at-po').value;
  const supplier    = document.getElementById('at-supplier-name').value;
  const wallet      = document.getElementById('at-supplier-wallet').value;
  const amount      = parseFloat(document.getElementById('at-amount').value);
  const desc        = document.getElementById('at-description').value;
  const acctEmail   = document.getElementById('at-accounts-email').value;
  const hrs         = parseInt(document.getElementById('at-deadline').value);

  const spec = [
    `Invoice Verification — ${po}`,
    `Supplier: ${supplier}`,
    `Amount: ${amount} RLUSD`,
    `Services: ${desc}`,
    ``,
    `PAYMENT RELEASES when the supplier submits an invoice matching:`,
    `- Supplier name: ${supplier}`,
    `- Amount: ${amount} RLUSD`,
    `- PO reference: ${po}`,
    `- Description consistent with: ${desc}`,
  ].join('\n');

  try {
    // POST to YOUR backend — wallet secret stays server-side
    const res = await fetch('/api/create-escrow', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        po_number: po, supplier_name: supplier,
        supplier_wallet: wallet, amount_rlusd: amount,
        task_description: spec, cancel_after_hrs: hrs,
        accounts_email: acctEmail,
      }),
    });
    const data = await res.json();
    if (data.escrow_id) {
      status.className = 'at-status success';
      status.innerHTML = `&#10003; Payment locked. Share with your supplier:<br>
        <strong>Escrow ID: ${data.escrow_id}</strong><br>
        Verified invoices will be sent to ${acctEmail}`;
    } else {
      throw new Error(data.detail || 'Unknown error');
    }
  } catch (err) {
    status.className = 'at-status error';
    status.textContent = `Error: ${err.message}`;
  }
}
</script>

Your suppliers use this to claim payment. They enter the escrow ID shared by the buyer, fill in their invoice, and submit. No AgentTrust account needed. This form calls the AgentTrust API directly — no wallet secret required on the supplier side.

<!-- AgentTrust Supplier Form — embed in your supplier portal -->
<style>
  .at-form { font-family: system-ui, sans-serif; max-width: 480px; }
  .at-form label { display: block; font-size: .85rem; font-weight: 600; margin-bottom: .3rem; }
  .at-form input, .at-form textarea {
    width: 100%; padding: .6rem .8rem; border: 1px solid #dde0ea;
    border-radius: 8px; font-size: .9rem; margin-bottom: 1rem; font-family: inherit;
  }
  .at-form button { padding: .65rem 1.5rem; background: #0066FF; color: #fff;
    border: none; border-radius: 8px; font-size: .9rem; font-weight: 700; cursor: pointer; }
  .at-status { margin-top: 1rem; font-size: .85rem; padding: .75rem 1rem;
    border-radius: 8px; display: none; }
  .at-status.success { background: #f0fdf8; border: 1px solid #bbf7d0; color: #166534; display: block; }
  .at-status.fail    { background: #fefce8; border: 1px solid #fde68a; color: #92400e; display: block; }
  .at-status.error   { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; display: block; }
</style>

<form class="at-form" onsubmit="atSubmitInvoice(event)">
  <h3>Claim Your Payment</h3>
  <p style="font-size:.85rem;color:#666;margin-bottom:1rem;">
    Enter the escrow ID your buyer shared with you, then fill in your invoice details.
  </p>

  <label>Escrow ID (from your buyer)</label>
  <input id="at-escrow-id" placeholder="e.g. INV-A1B2C3D4" required>

  <label>Your Invoice Number</label>
  <input id="at-inv-number" placeholder="INV-2025-0099" required>

  <label>Your Company Name</label>
  <input id="at-company" placeholder="Acme Logistics Ltd" required>

  <label>Amount Invoiced (RLUSD)</label>
  <input id="at-inv-amount" type="number" min="0.01" step="0.01" placeholder="500.00" required>

  <label>Services or Goods Delivered</label>
  <textarea id="at-inv-desc" rows="3"
    placeholder="Describe exactly what was delivered, matching your agreement with the buyer" required></textarea>

  <label>Purchase Order Reference <span style="font-weight:400;">(from your buyer)</span></label>
  <input id="at-po-ref" placeholder="PO-2024-0042">

  <label>Delivery Evidence URL <span style="font-weight:400;">(optional — tracking, photo, etc.)</span></label>
  <input id="at-evidence" type="url" placeholder="https://…">

  <button type="submit">Submit Invoice &amp; Claim Payment</button>
  <div class="at-status" id="at-supplier-status"></div>
</form>

<script>
async function atSubmitInvoice(e) {
  e.preventDefault();
  const status = document.getElementById('at-supplier-status');
  status.className = 'at-status';
  status.textContent = 'Submitting invoice for verification…';
  status.style.display = 'block';

  const escrowId = document.getElementById('at-escrow-id').value.trim();
  const invNum   = document.getElementById('at-inv-number').value.trim();
  const company  = document.getElementById('at-company').value.trim();
  const amount   = document.getElementById('at-inv-amount').value.trim();
  const desc     = document.getElementById('at-inv-desc').value.trim();
  const poRef    = document.getElementById('at-po-ref').value.trim();
  const evidence = document.getElementById('at-evidence').value.trim();

  const invoice = [
    `INVOICE ${invNum}`,
    `From: ${company}`,
    `Amount: ${amount} RLUSD`,
    poRef ? `PO Reference: ${poRef}` : '',
    `Services delivered: ${desc}`,
  ].filter(Boolean).join('\n');

  const body = { escrow_id: escrowId, work: invoice };
  if (evidence) body.evidence_links = [evidence];

  try {
    const res = await fetch('https://xrpl-referee.onrender.com/evaluate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    });
    const data = await res.json();

    if (data.verdict === 'PASS') {
      status.className = 'at-status success';
      status.textContent =
        `✓ Invoice verified (${data.score}/100). Payment released to your wallet automatically. ` +
        `A copy of your invoice has been forwarded to the buyer's accounts team.`;
    } else {
      status.className = 'at-status fail';
      status.textContent =
        `Invoice could not be verified (${data.score}/100). Reason: ${data.summary}. ` +
        `Please check the details and resubmit.`;
    }
  } catch (err) {
    status.className = 'at-status error';
    status.textContent = `Error: ${err.message}`;
  }
}
</script>

Server-side only: the buyer form posts to /api/create-escrow on your own backend — this is intentional. Your wallet secret must never be in browser JavaScript. Your backend holds the secret, pays the protocol fee, signs the EscrowCreate on XRPL, and returns the escrow_id. The supplier form calls AgentTrust directly — no wallet secret needed.

Server-side escrow creation

The accounts_email is stored in the escrow spec. When the referee returns a PASS verdict it emails the verified invoice to that address. You can also handle this yourself via a webhook callback.

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
import httpx, secrets
from xrpl.clients import JsonRpcClient
from xrpl.models.transactions import Payment, EscrowCreate
from xrpl.wallet import Wallet
from xrpl.utils import xrp_to_drops
from xrpl.transaction import submit_and_wait

app = FastAPI()

REFEREE         = "https://xrpl-referee.onrender.com"
PROTOCOL_WALLET = "rmcSrkpZ2i2kuvtCPeTVetee9SixP4djR"
buyer_wallet    = Wallet.from_seed("sYOUR_BUYER_SECRET")  # server-side only
client          = JsonRpcClient("https://s1.ripple.com:51234/")

class EscrowRequest(BaseModel):
    po_number:        str
    supplier_name:    str
    supplier_wallet:  str
    amount_rlusd:     float
    task_description: str
    accounts_email:   str           # verified invoices forwarded here
    cancel_after_hrs: int = 720
    callback_url:     Optional[str] = None   # optional webhook on PASS/FAIL

@app.post("/api/create-escrow")
async def create_escrow(req: EscrowRequest):
    # Embed accounts_email in the task spec so the referee knows where to forward
    full_spec = (
        req.task_description
        + f"\n\nAccounts email for verified invoices: {req.accounts_email}"
    )

    # 1. Pay protocol fee (0.1 XRP)
    fee_tx   = Payment(account=buyer_wallet.address,
                       amount=xrp_to_drops(0.1),
                       destination=PROTOCOL_WALLET)
    fee_hash = submit_and_wait(fee_tx, client, buyer_wallet).result["hash"]

    # 2. Generate escrow parameters
    escrow_id = f"INV-{secrets.token_hex(4).upper()}"
    body = {
        "escrow_id":        escrow_id,
        "fee_hash":         fee_hash,
        "buyer_name":       "Your Company",
        "buyer_address":    buyer_wallet.address,
        "worker_address":   req.supplier_wallet,
        "task_description": full_spec,
        "currency":         "RLUSD",
        "amount_rlusd":     req.amount_rlusd,
        "cancel_after_hrs": req.cancel_after_hrs,
        "category":         "invoice",
        "max_submissions":  5,
    }
    if req.callback_url:
        body["buyer_callback_url"] = req.callback_url

    params = httpx.post(f"{REFEREE}/escrow/generate", json=body, timeout=30).json()

    # 3. Sign and submit EscrowCreate on XRPL
    escrow_tx = EscrowCreate(
        account      = buyer_wallet.address,
        destination  = req.supplier_wallet,
        amount       = params["escrow_amount"],   # RLUSD token dict
        condition    = params["condition"],
        finish_after = params["finish_after_ripple"],
        cancel_after = params["cancel_after_ripple"],
    )
    tx_hash = submit_and_wait(escrow_tx, client, buyer_wallet).result["hash"]

    # 4. Confirm with referee (starts the clock)
    httpx.post(f"{REFEREE}/escrow/{escrow_id}/confirm",
               json={"tx_hash": tx_hash}).raise_for_status()

    return {"escrow_id": escrow_id, "tx_hash": tx_hash}
const express = require('express');
const { Client, Wallet, xrpToDrops } = require('xrpl');
const fetch   = require('node-fetch');
const crypto  = require('crypto');

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

const REFEREE         = 'https://xrpl-referee.onrender.com';
const PROTOCOL_WALLET = 'rmcSrkpZ2i2kuvtCPeTVetee9SixP4djR';
const BUYER_SECRET    = process.env.BUYER_WALLET_SECRET; // never hardcode

app.post('/api/create-escrow', async (req, res) => {
  const {
    po_number, supplier_name, supplier_wallet,
    amount_rlusd, task_description, accounts_email,
    cancel_after_hrs = 720, callback_url,
  } = req.body;

  // Embed accounts_email so the referee knows where to forward verified invoices
  const fullSpec = `${task_description}\n\nAccounts email for verified invoices: ${accounts_email}`;

  const client = new Client('wss://s1.ripple.com');
  await client.connect();
  const wallet = Wallet.fromSeed(BUYER_SECRET);

  // 1. Protocol fee
  const feeTx = await client.submitAndWait({
    TransactionType: 'Payment',
    Account: wallet.address,
    Destination: PROTOCOL_WALLET,
    Amount: xrpToDrops('0.1'),
  }, { wallet });

  // 2. Generate escrow parameters
  const escrowId = `INV-${crypto.randomBytes(4).toString('hex').toUpperCase()}`;
  const body = {
    escrow_id: escrowId, fee_hash: feeTx.result.hash,
    buyer_name: 'Your Company', buyer_address: wallet.address,
    worker_address: supplier_wallet, task_description: fullSpec,
    currency: 'RLUSD', amount_rlusd, cancel_after_hrs,
    category: 'invoice', max_submissions: 5,
  };
  if (callback_url) body.buyer_callback_url = callback_url;

  const params = await fetch(`${REFEREE}/escrow/generate`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  }).then(r => r.json());

  // 3. Submit EscrowCreate
  const escrowTx = await client.submitAndWait({
    TransactionType: 'EscrowCreate',
    Account: wallet.address,
    Destination: supplier_wallet,
    Amount: params.escrow_amount,
    Condition: params.condition,
    FinishAfter: params.finish_after_ripple,
    CancelAfter: params.cancel_after_ripple,
  }, { wallet });

  // 4. Confirm with referee
  await fetch(`${REFEREE}/escrow/${escrowId}/confirm`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ tx_hash: escrowTx.result.hash }),
  });

  await client.disconnect();
  res.json({ escrow_id: escrowId, tx_hash: escrowTx.result.hash });
});

app.listen(3000);

Full automation — no humans required

For fully automated AP, both sides run code. The buyer side triggers on a delivery event; the supplier side triggers on an escrow-ready webhook. Each operates independently — neither needs to know the other's implementation.

Your warehouse management system, ERP, or delivery platform fires a webhook when a delivery is confirmed. This handler creates the escrow automatically and notifies the supplier — no human AP involvement.

from fastapi import FastAPI
from pydantic import BaseModel
import httpx, secrets, smtplib
from email.message import EmailMessage
from xrpl.clients import JsonRpcClient
from xrpl.models.transactions import Payment, EscrowCreate
from xrpl.wallet import Wallet
from xrpl.utils import xrp_to_drops
from xrpl.transaction import submit_and_wait

app = FastAPI()
REFEREE         = "https://xrpl-referee.onrender.com"
PROTOCOL_WALLET = "rmcSrkpZ2i2kuvtCPeTVetee9SixP4djR"
buyer_wallet    = Wallet.from_seed("sYOUR_BUYER_SECRET")
client          = JsonRpcClient("https://s1.ripple.com:51234/")

class DeliveryConfirmed(BaseModel):
    """Payload from your WMS/ERP when a delivery is confirmed."""
    po_number:        str
    supplier_name:    str
    supplier_wallet:  str
    supplier_email:   str          # to notify the supplier
    amount_rlusd:     float
    description:      str
    accounts_email:   str = "accounts@yourcompany.com"

@app.post("/webhooks/delivery-confirmed")
async def on_delivery_confirmed(event: DeliveryConfirmed):
    """Triggered automatically when WMS marks a delivery as received."""

    spec = (
        f"Invoice Verification — {event.po_number}\n"
        f"Supplier: {event.supplier_name}\n"
        f"Amount: {event.amount_rlusd} RLUSD\n"
        f"Services: {event.description}\n\n"
        f"PAYMENT RELEASES when the invoice matches the above.\n"
        f"Accounts email for verified invoices: {event.accounts_email}"
    )

    # 1. Protocol fee
    fee_hash = submit_and_wait(
        Payment(account=buyer_wallet.address,
                amount=xrp_to_drops(0.1),
                destination=PROTOCOL_WALLET),
        client, buyer_wallet
    ).result["hash"]

    # 2. Generate escrow
    escrow_id = f"INV-{secrets.token_hex(4).upper()}"
    params = httpx.post(f"{REFEREE}/escrow/generate", json={
        "escrow_id":        escrow_id,
        "fee_hash":         fee_hash,
        "buyer_name":       "Your Company",
        "buyer_address":    buyer_wallet.address,
        "worker_address":   event.supplier_wallet,
        "task_description": spec,
        "currency":         "RLUSD",
        "amount_rlusd":     event.amount_rlusd,
        "cancel_after_hrs": 720,
        "category":         "invoice",
        "max_submissions":  5,
    }, timeout=30).json()

    # 3. Submit EscrowCreate
    tx_hash = submit_and_wait(EscrowCreate(
        account=buyer_wallet.address, destination=event.supplier_wallet,
        amount=params["escrow_amount"], condition=params["condition"],
        finish_after=params["finish_after_ripple"],
        cancel_after=params["cancel_after_ripple"],
    ), client, buyer_wallet).result["hash"]

    # 4. Confirm
    httpx.post(f"{REFEREE}/escrow/{escrow_id}/confirm",
               json={"tx_hash": tx_hash}).raise_for_status()

    # 5. Email the supplier with their claim link
    _send_supplier_email(event.supplier_email, event.supplier_name,
                         escrow_id, event.po_number, event.amount_rlusd)

    return {"escrow_id": escrow_id}

def _send_supplier_email(to, name, escrow_id, po, amount):
    msg = EmailMessage()
    msg["Subject"] = f"Payment ready to claim — {po}"
    msg["From"]    = "noreply@yourcompany.com"
    msg["To"]      = to
    msg.set_content(
        f"Hi {name},\n\n"
        f"Your payment of {amount} RLUSD for {po} is locked and ready to claim.\n\n"
        f"Visit our supplier portal and enter your Escrow ID: {escrow_id}\n\n"
        f"https://yourcompany.com/supplier-portal\n\n"
        f"Submit your invoice and payment will be released automatically."
    )
    # Configure your SMTP here
    # with smtplib.SMTP("smtp.yourcompany.com") as s: s.send_message(msg)

The supplier's system listens for a webhook from AgentTrust when an escrow is created for their wallet. It auto-generates a conforming invoice and submits it immediately — no human involvement on the supplier side.

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
import httpx
from datetime import date

app = FastAPI()
REFEREE = "https://xrpl-referee.onrender.com"

class EscrowReadyWebhook(BaseModel):
    """Webhook payload from AgentTrust when an escrow is created for your wallet."""
    escrow_id:        str
    amount_rlusd:     Optional[float] = None
    amount_xrp:       Optional[float] = None
    task_description: str
    buyer_name:       str

# AgentTrust calls this URL when an escrow targets your wallet address.
# Set buyer_callback_url on your side, or register a webhook in your profile.
@app.post("/webhooks/escrow-ready")
async def on_escrow_ready(event: EscrowReadyWebhook):
    """Auto-generate and submit a conforming invoice."""

    amount = event.amount_rlusd or event.amount_xrp
    currency = "RLUSD" if event.amount_rlusd else "XRP"

    # Generate invoice from your own records (billing system, job data, etc.)
    invoice_number = f"INV-{date.today().strftime('%Y%m%d')}-{event.escrow_id[-4:]}"

    # Build the invoice text — must match the escrow task_description
    invoice = _generate_invoice(invoice_number, event.buyer_name, amount, currency, event.task_description)

    # Submit to AgentTrust — PASS releases payment automatically
    result = httpx.post(f"{REFEREE}/evaluate", json={
        "escrow_id": event.escrow_id,
        "work":      invoice,
    }, timeout=120).json()

    verdict = result.get("verdict")
    score   = result.get("score")
    print(f"Escrow {event.escrow_id}: {verdict} ({score}/100)")

    if verdict == "PASS":
        print(f"Payment released. Invoice {invoice_number} accepted.")
    else:
        # FAIL — log the reason; could auto-correct and resubmit
        print(f"Invoice rejected: {result.get('summary')}")
        # Optional: alert a human, or adjust and resubmit

    return {"status": verdict, "score": score}

def _generate_invoice(inv_num, buyer_name, amount, currency, task_desc):
    """Build a conforming invoice from your billing records."""
    return "\n".join([
        f"INVOICE {inv_num}",
        f"Date: {date.today().isoformat()}",
        f"From: Your Supplier Company Ltd",
        f"To: {buyer_name}",
        f"Amount: {amount} {currency}",
        f"",
        f"Services delivered:",
        # Extract the key details from the task description to match exactly
        task_desc.split("Services:")[1].split("\n")[0].strip()
            if "Services:" in task_desc else task_desc[:200],
    ])

MCP shortcut: if your agent is MCP-compatible (Claude, GPT-4o with MCP, etc.), skip writing REST calls entirely. Connect the AgentTrust MCP server and instruct your agent: "Submit an invoice for escrow INV-XXXX for 500 RLUSD, delivered coffee to warehouse." It calls evaluate_escrow_work automatically.