Send a construction pay application or change order PDF. Get back the figures as JSON, with arithmetic checks. $0.20 in USDC per successful read over x402, on Base or Solana. No account, no API key, no subscription. Failed reads are never charged.
There is no reviewer on a paid read, so every response carries the checks a reviewer would make — the G702's own arithmetic, the first-application gross rule, and the retainage-billing rule — plus plain-language warnings when one fails. Verify figures against the document before relying on them.
1. Describe the service (free):
curl https://subpay.ai/api/v1/extract2. Call it with any x402 client. The first request gets 402 Payment Required naming the price and where to pay; the client pays and retries automatically. In TypeScript, with @x402/fetch:
import { readFileSync } from "node:fs";
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
// A wallet holding USDC on Base. It needs no ETH: the facilitator pays gas.
const account = privateKeyToAccount(process.env.WALLET_PRIVATE_KEY as `0x${string}`);
const pay = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
});
const res = await pay("https://subpay.ai/api/v1/extract", {
method: "POST",
headers: { "Content-Type": "application/pdf" },
body: readFileSync("pay-app.pdf"),
});
console.log(res.status, await res.json());Paying on Solana instead — register the Solana scheme:
import { ExactSvmScheme } from "@x402/svm/exact/client";
import { toClientSvmSigner } from "@x402/svm";
const pay = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [{
network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
client: new ExactSvmScheme(toClientSvmSigner(yourSolanaKeypairSigner)),
}],
});The same service is an MCP server at https://subpay.ai/mcp (Streamable HTTP), for agents that use tools rather than call web APIs. Tools: extract_document (paid, $0.20 per successful call; the PDF base64-encoded in document, optional fileName, subcontractor, gc) and about (free). Payment is per tool call over x402; a call that fails is not charged.
import { createx402MCPClient } from "@x402/mcp";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const client = createx402MCPClient({
name: "my-agent",
version: "1.0.0",
schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
autoPayment: true,
});
await client.connect(new StreamableHTTPClientTransport(new URL("https://subpay.ai/mcp")));
const result = await client.callTool("extract_document", {
document: readFileSync("pay-app.pdf").toString("base64"),
});POST https://subpay.ai/api/v1/extract with the PDF in any of three forms:
Content-Type: application/pdffiledocument:POST https://subpay.ai/api/v1/extract
Content-Type: application/json
{
"document": "<the PDF, base64-encoded>",
"fileName": "pay-app-27.pdf",
"subcontractor": "Acme Signs LLC",
"gc": "Skanska"
}subcontractor and gc are optional (JSON fields, or query parameters with the other two forms). Naming the parties sharpens telling a subcontractor's request from the GC's change order.
A pay application:
{
"document": {
"documentType": "PAY_APPLICATION",
"applicationNumber": "27",
"periodTo": "2026-06-30",
"grossThisPeriod": 41819.65,
"retainageHeldToDate": 4181.96,
"retainageReleasedThisPeriod": null,
"retainageRatePercent": 10,
"totalCompletedAndStoredToDate": 41819.65,
"contractSumToDate": 63388,
"balanceToFinishIncludingRetainage": 25750.31,
"currentPaymentDue": 37637.69,
"changeOrders": []
},
"checks": [
{ "name": "g702_math", "passed": true,
"detail": "contract sum to date − completed to date (+ retainage held) = balance to finish" },
{ "name": "first_application_gross", "passed": true,
"detail": "first application: gross this period should equal total completed & stored to date" }
],
"warnings": [],
"confidence": 0.95
}A change order:
{
"document": {
"documentType": "CHANGE_ORDER",
"kind": "GC_CHANGE_ORDER",
"issuer": "Balfour Beatty",
"number": "004",
"description": "CE #705 - WTC Garage Scope Gaps - Clearance Bar at 10th Floor Parking Garage",
"amount": 2405.69,
"date": "2025-09-14"
},
"checks": [],
"warnings": [],
"confidence": 0.95
}kind is SUBCONTRACTOR_REQUEST for a request for change order (not yet part of the contract) or GC_CHANGE_ORDER for the general contractor's executed change order. On a retainage billing — no new work, held retainage being paid out — grossThisPeriod is 0 and the payout is in retainageReleasedThisPeriod. Figures not printed on the document are null.
| Network | CAIP-2 id | Asset |
|---|---|---|
| Base | eip155:8453 | USDC 0x8335…2913 |
| Solana | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp | USDC EPjF…Dt1v |
None of these are charged.
| 400 | No PDF in the request, or the wrong field name |
| 402 | Payment required — your x402 client handles this |
| 413 | PDF larger than 4 MB |
| 415 | The body isn't a PDF |
| 502 | The document couldn't be read |
| 503 | Reading temporarily unavailable |
Machine-readable: OpenAPI spec and llms.txt.
Use of the API is governed by the API Terms of Use and the Privacy Policy. Questions: support@subpay.ai.