Verify a Receipt
Every Vertical Marketplace API response is wrapped in a provenance envelope with an Ed25519-signed receipt. Paste any response below to check its signature against the platform's public key — no trust in us required.
The receipt signs the message keyId.signedAt.digest, where digest = sha256 of the response body with the provenance block removed. We verify the signature and, when the payload is present, re-compute the digest to confirm the body hasn't been altered.
Prefer to check on your own machine? Download verify-receipt.mjs (one file, no dependencies, Node 18+) and run node verify-receipt.mjs receipt.json. It handles VM Pay receipts, platform receipts and provenance envelopes. This checks a signature. It does not prove a sale was arm's-length or that any party is independent.
Paste a VM Pay checkout session id and we verify its stored receipt server-side in one step — useful when a counterparty handed you an id, not the receipt itself.
Platform public key
Fetch it programmatically at GET /api/signing-key, or verify server-side by POSTing to /api/verify-receipt.
Verify offline — without trusting us
A receipt that only verifies against our server is a log entry. Save the public key once, then check any VM Pay receipt on your own machine — works even if verticalmarketplace.ai is unreachable:
// node verify.js — no dependencies beyond Node 18+
const crypto = require("node:crypto");
const receipt = require("./receipt.json"); // GET /api/vmpay/receipts/:sessionId
const publicKeyPem = require("./key.json").publicKeyPem; // GET /api/vmpay/signing-key (save once)
// Canonical JSON: keys sorted at every level, so key order never matters.
const stable = (v) =>
v === null || typeof v !== "object" ? JSON.stringify(v)
: Array.isArray(v) ? `[${v.map(stable).join(",")}]`
: `{${Object.keys(v).sort().map((k) => `${JSON.stringify(k)}:${stable(v[k])}`).join(",")}}`;
const { signature, provenance, ...body } = receipt;
const message = Buffer.concat([
Buffer.from(`${signature.keyId}.${signature.signedAt}.`),
Buffer.from(stable(body)),
]);
const ok = crypto.verify(null, message, crypto.createPublicKey(publicKeyPem),
Buffer.from(signature.signature, "base64url"));
console.log(ok ? "RECEIPT VERIFIED" : "TAMPERED OR FORGED");Marketplace provenance envelopes verify the same way over keyId.signedAt.digest — see /api/signing-key for that key.