← All updates

x402 wallet payments on /api/v1/enhance

Wallet callers can now pay for /api/v1/enhance in USDC. The 402 challenge declares a per-page price ($0.20 / page) computed from the uploaded PDF, so the amount quoted is always the amount settled.

apibillingenhancex402

POST /api/v1/enhance now accepts x402 wallet payments alongside Bearer tokens and session cookies. This was the last of the billed /api/v1 endpoints still gated to credit callers; all three (/md, /render, /enhance) now share the same payment surface.

The wallet rate is $0.20 / page in USDC — the same as the credit list price (1 credit / page at $0.20 / credit). No x402 discount, no x402 premium, so the two paths can be repriced independently later.

Because the price is per-page, the 402 challenge body has to know the page count before it's built. The route now parses the uploaded PDF first; the amount declared in the challenge is pageCount × 200000 atomic USDC, and that's exactly what gets settled if the wallet pays.

{
  "x402Version": 2,
  "error": "Payment required",
  "resource": {
    "url": "https://makespdf.com/api/v1/enhance",
    "description": "Enhance a non-compliant PDF to PDF/A-2A + PDF/UA-1 ($0.20 / page)",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "amount": "1000000",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "payTo": "0x…"
    }
  ]
}

A few wrinkles worth calling out for wallet callers:

  • Idempotent retry, with one carve-out. Settled wallet renders are cached by signature hash for 5 minutes; a duplicate PAYMENT-SIGNATURE replays the cached JSON envelope verbatim with X-Idempotent-Replay: 1 and never re-runs the pipeline. The exception is mode: "both", which packs two base64 PDFs into one envelope and can blow the 25 MB Workers KV value cap — those responses skip the cache, so a retried mode: "both" request re-pays.
  • Refunds. Validation rejections (malformed PDF, admin-only options, unmatched alt-text overrides) leave the payment in pending for the daily refund cron. Render-stage failures and partial-regenerate outcomes are explicitly marked failed so the cron picks them up faster.
  • options.model stays admin-only. Wallet callers don't have a user row, so they can't be admins; passing options.model returns 403.