A hosted virtual supplier for testing e-procurement integrations. cXML PunchOut, OCI, order confirmation, dispatch notification, and the invoice flow back — pointed at by anyone building a buyer-side punchout client who has nowhere else to point it.
It also answers the question a validator cannot: what survives ingestion.
cXML imposes almost no length limits, so every limit that truncates your data
is imposed by the buyer platform and none of them publish it.
/ingest applies what Ariba, Coupa,
JAGGAER and Oracle each do on receipt and shows you what is left.
Live at punchoutsandbox.com. Free, no paid tier, MIT.
Every free punchout tool in existence runs the wrong way. They simulate a buyer so that a supplier can check their catalogue works. Nobody built the mirror image, so a developer writing the buyer side has nothing to test against — and can only test their integration against itself.
That distinction is the whole point, and it is worth being precise about:
Round-tripping your own
build→extractproves the two halves agree with each other. It does not prove either conforms to the spec. If both share a misreading, every test passes and the first real supplier rejects every document.
What is missing is an independent judge — and, per RESEARCH.md, no vendor or network anywhere issues one. TradeCentric, Greenwing, Cloudfy, Coupa, SAP Ariba and Jaggaer all do bilateral, buyer-coordinated sign-off instead. There is no portable conformance report at any price.
This service is that judge. Everything else here is a plausible-looking shop that exists so the judge has something to judge.
- Serves a real punchout session. Point your cXML
PunchOutSetupRequestor OCI call-up at it, browse a catalogue of 96 products with genuine UNSPSC codes, return a conformant cart. Full OCI support includingDETAIL,VALIDATEandBACKGROUND_SEARCH. - Validates every document against the actual cXML DTDs (vendored 1.2.071), in both directions, and tells you exactly what is wrong and where — every error with its line number, element and a hint.
- Separates errors from advisories. Errors come from the DTD and are not a
matter of opinion. Advisories are things a DTD cannot express but that break
real integrations — a dropped
SupplierPartAuxiliaryID, mixed currencies, totals that do not add up,eachwhereEAwas meant. - Runs the whole downstream flow: purchase order in, then order confirmation, dispatch notice and invoices with multi-country tax (VAT, GST, sales tax, reverse charge) across 27 jurisdictions — POSTed to your own cXML inbox so you can test the half nobody can normally test.
- Shows what survives ingestion.
/ingestapplies what each buyer platform silently truncates, rounds or defaults. - Hands you documents built to break your parser.
/samplesserves a worked example of every message, plus an adversarial set: conformant cXML that disagrees with the order in exactly one way a real supplier really does — despatched in a different unit, partially shipped, confirmed at a changed price. - Has a machine path. JSON API, credentials in headers, no browser anywhere — see below.
Live and in use. Deployed on AWS Lambda behind Cloudflare, £0/month, no billing and no SLA. See BRIEF.md §8 for the honest commercial read: this is deliberately a free tool, not a product. It is worth more as credibility and reach into the audience that buys procurement software than as revenue.
| Protocols | cXML PunchOut, SAP OCI |
| Documents | PunchOutSetupRequest · PunchOutOrderMessage · OrderRequest · ConfirmationRequest · ShipNoticeRequest · InvoiceDetailRequest |
| Tests | 13 suites, plus a 147-check QA suite that runs against the deployed site |
| Not built | multipart/related attachments; OCI SOURCING (SAP never implemented it either) |
Its first serious use by an outside integrator found six defects in their implementation in an afternoon — and three in this one, all since fixed and each now pinned by a test.
app/ the Lambda application
xml_safe.py THE hardened parser — nothing else may touch raw XML
validation.py the independent judge; this module is the product
cxml/dtd/ vendored cXML 1.2.071 DTDs (see that directory's README)
ui/ design system and templates
api.py the JSON machine path
platforms.py what each buyer platform does to a cart on ingestion
samples.py worked and adversarial example documents
infra/ Python CDK — DataStack + SiteStack
scripts/ Cloudflare DNS, edge secret, SES, search verification
BRIEF.md the idea, the case against, and the verdict
RESEARCH.md market evidence behind that verdict
HOSTING.md hosting, the AWS account layout, and the QA findings
INTEGRATING.md quick start for buyer-side developers
docs/reference/ the conformance material, published at /reference
INTEGRATING.md is the quick start: credentials, the JSON API, the four integration points, and the five things that otherwise cost an afternoon. The full documentation goes deeper.
No browser needed at any point:
curl -sX POST https://punchoutsandbox.com/api/signup -d '{"email":"you@company.com"}'Then POST /api/validate with X-Sandbox-Identity / X-Sandbox-Secret
headers returns the full conformance report as JSON — every error with its
line, element and hint. POST /api/ingest returns what each buyer platform
would do to a cart. Worked, DTD-valid examples of every document are at
/samples.
The punchout round trip is headless too: a live punchout session authorises the storefront on its own, because the shopper is the buyer's employee and the buyer's system already authenticated for them.
python3 -m venv .venv && .venv/bin/pip install -r requirements.txtThen start the dev server — it uses in-memory stores, so no AWS credentials are needed:
.venv/bin/python -m app.handlerEach suite is a plain script that prints what it checked and why, and exits non-zero on failure. No test runner, no fixtures to learn.
for t in tests/test_*.py; do .venv/bin/python "$t" || break; doneThey are worth reading as documentation. test_orders.py and
test_invoice.py validate every generated document against the real cXML
DTDs, which is the claim this whole project rests on;
test_dtd_licence.py enforces the terms those DTDs ship under.
cd infra && cdk deploy --all && python scripts/deploy_cloudflare_dns.pyCloudflare credentials come from the environment (CLOUDFLARE_API_TOKEN,
CLOUDFLARE_ACCOUNT_ID) and are never committed. See the deploy script's
docstring.
This project's own code is MIT — see LICENSE.
The vendored cXML DTDs in app/cxml/dtd/ are not. They are copyright
Ariba, Inc. and ship under the cXML License
Agreement, which permits copying and
distributing the unmodified Specification provided the licence is attached.
Both conditions are enforced by tests/test_dtd_licence.py rather than left as
good intentions: it checksums every DTD and fails if one is edited.
Practical upshot: MIT's permission to modify stops at those files. If a DTD needs changing, the change belongs upstream, not here.
The licence URL printed inside the DTDs themselves (cxml.org/home/license.asp)
is dead and returns 404; the live agreement is at
cxml.org/license.html.
app/cxml/dtd/README.md has the full reasoning.
Generated invoices and PDFs are synthetic, always watermarked as test artifacts, and always for invented companies. Never generate documents carrying a real company's name or branding.