Inspiration

Supply chains still run on EDI. Once an autonomous agent is allowed to pay invoices, a single injected note in an 810 ("remit to this new account") or a partial shipment billed in full is enough to move real money. We wanted the opposite of "the agent decided": money moves through tinyGAAP only when the purchase order, the receiving advice and the invoice agree, and every approval leaves a signed receipt that someone else can check.

What it does

  • x402-style HTTP 402 gate (not wire-compatible with the x402 spec): HTTP settlement requests must carry an HMAC-SHA256 signature (shared key) bound to the exact SHA-256 of the raw EDI 810. A missing or forged signature fails closed with 402 Payment Required.
  • True 3-way match by default: EDI 850 (purchase order) ↔ 861 (receiving advice) ↔ 810 (invoice), matched by vendor SKU in any line order, prices in integer cents and quantities against what was received, with a per-PO, per-SKU billed-quantity tally, so the same PO can't be billed twice under different invoice numbers. Every invoice that passes the signature gate gets an EDI 997 acknowledgment, and a rejected invoice line is named by its exact segment position.
  • Fail-closed auditor: deterministic rules scan NTE notes for known injection phrases, off-domain payee handles and third-party notes, and require the CAD pay-to and N1*SE seller ID to match the purchase order's seller, which must be on the allowlist. An optional LLM can add a second opinion, but it can only veto. If the LLM errors or answers malformed JSON, the invoice is rejected; an LLM "approve" never overrides the rules or the match.
  • PayPal Orders v2 settlement: creates the order with idempotency keys and custom_id bound to the SHA-256 of the originating PO, waits for the buyer's approval, reads the order back and checks its ID, status, custom_id, invoice_id, currency and amount, and emits a signed EDI 820 remittance only when PayPal reports the capture COMPLETED for the same order and amount.

How we built it

One human, a team of AI agents, and a rule: nobody grades their own homework. Gemini (Google), through Google Antigravity, wrote most of the Go implementation and its tests, using only the standard library. Several Claude (Anthropic) agents, independent of the implementation, attacked it through Claude Code: a six-lens review that raised 25 findings (on the submitted tree, 22 are verified closed; the rest are covered under the README's Known limitations), a mutation-testing harness (on the submitted tree, 61 of 63 applicable mutants are caught; one of the two survivors is a deliberate equivalence control), an EDI cross-check against public sources, a license and attribution check, and a secret and privacy scan of everything reachable in the public repository. The repository also ships a 12-case adversarial corpus (injection, payee hijacking, a forged signature, arithmetic and envelope tampering) that the test suite replays. Each release candidate was rebuilt and tested by a second agent, which reported (test results plus a content hash) before anything moved forward. Carlos Fuentes designed the system, set the acceptance criteria, and made every release decision.

Challenges we ran into

  • A PayPal Orders v2 order created the way ours is (no saved payment method) needs the buyer's approval before it can be captured, so our capture isn't fully headless. We record that honestly instead of faking it: our engine waits for a person to approve the sandbox order, then captures it, and the committed proof comes from that live run and verifies offline against keys/.
  • Making an LLM useful without letting it decide: it got veto power only, with fail-closed handling of every error.
  • Keeping AI agents honest with each other: a build or test result counts only when a different agent has reproduced it.

Accomplishments that we're proud of

  • Zero external dependencies: Go standard library only, and it builds and tests offline on Linux and macOS.
  • Fail-closed at each check: signature, match, auditor and capture.
  • A signed remittance exists only after PayPal itself says COMPLETED.
  • Every release candidate was rebuilt and checked by a second agent before anything moved forward.
  • Receipts are signed with a durable Ed25519 key whose public half is published in keys/, so anyone can verify them offline. A demo run signs with a throwaway key labelled kid=efimera, which the registry verifiers always reject.
  • Settled invoices live in an append-only, hash-chained ledger that is re-verified at startup: a restart alone can't be used to bill twice, and an edited entry keeps the engine from starting (deleting entries from the end is caught by receipt holders, not by the engine).

What we learned

  • An LLM works best as a skeptic with veto power, not as an approver.
  • A signature proves something only if its key outlives the process that made it. Our own review caught that, and we fixed it before shipping.

What's next

  • Incoterms®-aware attestation: Incoterms® rules decide who bears the risk and the costs, your local GAAP decides the books, and tinyGAAP simply attests (who pays freight, insurance and duties under the agreed term, checked against the purchase order).
  • Signed, verified invoices that a factoring partner can rely on.
  • An MCP front door, so agents can call tinyGAAP next to PayPal's own MCP tools.
  • Route each capture to the verified supplier's PayPal account (today the order sets no payee), more transaction sets, key rotation with an encrypted off-site backup, and a hosted sandbox for judges and early users.

tinyGAAP is an independent project; it is not affiliated with, endorsed by, or certified by ASC X12, PayPal, Inc., or the x402 project. tinyGAAP is not an audit or attestation engagement.

Built With

  • claude
  • claude-code
  • ed25519
  • edi
  • gemini
  • go
  • google-antigravity
  • hmac-sha256
  • http-402
  • paypal-orders-v2
  • the-receiving-advice-and-the-invoice-agree
Share this project:

Updates

Submission history