# Interface Consistency Check v1 agent API

This API prepares a private report before one exact programmatic payment. Use `https://lumenhouse.xyz` exactly; www, redirects and query-string capabilities are unsupported for this API. AI agents are the intended customers. User-Agent strings grant no access or payment authority.

One purchase processes one supplied document snapshot. Choose exactly **USD 1.00 through Stripe MPP (100 cents)** or **0.10 USDC through x402 v2 on Base (100000 atomic units)**. These are distinct fixed prices, not exchange-rate conversions. No recurring charge or paid model call is involved.

## Prepare, pay once, recover

1. Generate 32 cryptographically random bytes encoded as 64 lowercase hex characters. Persist this private capability, the input and the intended payer identity locally before submitting anything. The executable reference client uses a private 0700 directory and 0600 files. It saves the purchase journal with atomic renames and file/directory fsync, and publishes complete fsynced lock and dispatch-marker records using exclusive hard links. Keep these files; losing the capability can prevent recovery.
2. POST `/api/interface-check/orders` with `Content-Type: application/json` and `X-Lumen-Recovery-Key: <saved capability>`. The body has exactly `payment_method` (`stripe` or `x402`) and `input` (the supplied snapshot). It validates the request, generates a report and persists its exact bytes before producing a payment quote. **HTTP 402 means successful preparation.** It returns no report findings or raw submitted contents. Unusable requests fail before payment.
3. Verify the product, version, input/report SHA-256, report byte count, price, merchant identity and purchase binding. Persist the order and quote. Stripe quotes include `WWW-Authenticate` and `quote.challenge`; x402 quotes include `PAYMENT-REQUIRED` and `quote.payment_required`. Quotes last five minutes. These headers are also in `quote.headers`.
4. Save a permanent local payment-dispatch marker **before** calling a signer, an SPT issuer or a payment transport. Submit one purchase-bound proof by POST `/api/interface-check/orders/{order_id}/pay`, with `Authorization: Bearer <saved capability>`. Stripe uses `X-Lumen-Payment: Payment <credential>`; x402 uses `PAYMENT-SIGNATURE: <base64 payload>`. Authorization remains reserved for the private recovery capability. The server persists payment-attempt state before provider dispatch and reconciles provider evidence before release.
5. GET `/api/interface-check/recovery` with only `Authorization: Bearer <saved capability>` if the original order ID or any response was lost. This locates the existing order without a new charge. GET `/api/interface-check/orders/{order_id}` performs the same private read for a known ID. Never attach a financial proof to recovery reads. Submitted, uncertain or review states do not authorize another payment.
6. Once the order is `confirmed` (200), GET `/api/interface-check/orders/{order_id}/report` with Bearer authorization. Verify its SHA-256 and byte count against order metadata and `X-Lumen-Report-SHA256`. The response is the exact originally prepared deterministic JSON, with `Cache-Control: private, no-store`. Save it locally. Each recovery returns those same bytes while retained.

An unattempted expired quote can be refreshed by POST to the existing `/pay` route **without either payment-proof header**, using the saved Bearer capability. This is a quote-only operation and creates no payment. The client exposes an explicit `refresh` command. A local dispatch marker blocks refresh/payment reuse after an attempt. Never use a general-purpose auto-paying fetch wrapper or a CLI that silently retries the 402 loop for this purchase.

## Request and structured result

```json
{
  "payment_method": "stripe",
  "input": {
    "schema_version": "lumen-interface-check-input/1",
    "base_url": "https://example.com",
    "llms_txt": "# Example shop\n\n> Public interface snapshot.\n",
    "openapi": {
      "openapi": "3.1.0",
      "info": {"title": "Example shop", "version": "1.0.0"},
      "servers": [{"url": "https://example.com"}],
      "paths": {}
    },
    "offers": []
  }
}
```

The complete prepare/input/result/order/error schemas and fictional examples are linked from the offer and OpenAPI. Prepare and recovery order metadata include `schema_version`, `product_id`, `order_id`, `status`, `checker_version`, `input_sha256`, `report_sha256`, `report_bytes`, `binding_sha256`, `payment_method` and `price`. Prepared orders include `quote`; confirmed orders include a sanitized `receipt` and `recover_until`. Credentials and capability values are never returned.

Report fields are `schema_version: lumen-interface-check-result/1`, `ruleset_version: 1.0.0`, `status: consistent|findings`, severity counts and a findings array. Each finding has a stable `code`, `severity`, source `document`, JSON/text `pointer`, `message` and a suggested `correction` (`target`, `pointer`, `action`, optional `value`). Documentation inconsistencies are successful paid findings. Suggestions require caller review; no automatic editing occurs. Equivalent effective inputs with checker version 1.0.0 produce identical report bytes.

Selected rules cover llms.txt navigation, URLs and methods, OpenAPI 3.0/3.1 structures/local references, offer identity/availability, schema fields, artifact metadata and decimal-to-atomic/minor price units. This is not full OpenAPI conformance, a security certification, live availability, link reachability, runtime behavior or payment-settlement auditing of the supplied documents. OpenAPI 3.2, YAML, callbacks/webhooks, external reference fetching and arbitrary prose interpretation are unsupported. No outbound document fetching is performed.

## Limits, status and errors

The entire prepare request body is at most 524288 UTF-8 bytes, including its wrapper. The input has at most 65536 llms.txt bytes, 393216 canonical OpenAPI JSON bytes, 20 offers, depth 32 and 20000 nodes. Reports have at most 500 findings and 262144 bytes. The findings cap includes an explicit overflow finding. A valid input producing a larger report is rejected before payment.

Unpaid private reports expire after 24 hours. Confirmed reports have the 30-day recovery period beginning at confirmation. Report access ends at expiry; bounded maintenance deletes the report bytes and can lag during shared quota exhaustion. Compact order, recovery-hash and attempted-payment audit markers remain so the same proof/capability cannot become a new paid job. Submitted or uncertain reports and reconciliation records remain until resolved, counting against fixed capacity. Admission rejects new preparation at 128 unpaid reports, 32 MiB unpaid report bytes, 256 MiB total report bytes or 32768 permanent audit rows. Physical storage admission also checks the SQLite byte count and conservative reservations for reports, metadata, indexes and later payment-state changes against a 768 MiB application ceiling. Order metadata is bounded at 16 KiB.

Preparation is limited to four calls per connecting-IP hash per minute and 32 globally per hour. While the normal write allocation remains available, payment, quote-refresh and unresolved-recovery admission is limited to 60 per connecting-IP hash per minute and 300 globally per hour. Retained confirmed reads and attempted-order recovery after normal write-budget exhaustion use separate best-effort per-instance limiters at those read rates, without writing rate counters; instance restarts reset that fallback limiter. The checker reserves at most 20000 SQL/alarm writes per UTC day, leaving 80000 of the account's published daily allowance for other applications. This is the checker's allocation, not a reservation made by Cloudflare or a guarantee about shared account availability. After the one-time budgeted schema bootstrap, unknown capabilities are denied before persistent admission bookkeeping. Budget exhaustion prevents new financial dispatch; confirmed retained reports remain readable without persistent bookkeeping. A 429 or write-budget 503 response does not authorize a fresh payment; wait and retry the original private recovery read. Cleanup never converts an unresolved payment into permission for another charge.

| HTTP | Meaning | Caller action |
|---|---|---|
| 200 | Confirmed metadata, or paid report bytes | Verify and save report |
| 202 | Dispatching, uncertain or review | Bearer recovery only; no new payment |
| 402 | Prepared report and quote, or unpaid report withheld | Verify exact quote; pay once only with separate authority |
| 400 | Malformed/unsupported input or proof contract | Correct input before any payment; preserve attempted jobs |
| 401 / 428 | Invalid/missing Bearer capability / missing prepare capability | Use the saved private capability |
| 403 / 421 | Origin rejected / canonical origin required | Use the exact API origin |
| 404 / 405 | Unknown route / method | Follow OpenAPI |
| 409 | Existing recovery capability conflicts with snapshot or method | Preserve the original job; do not reuse capability |
| 410 | Quote or private report expired | Quote-only refresh for unattempted retained jobs; expired reports unavailable |
| 413 / 415 | Size limit / unsupported content type | Submit a bounded JSON snapshot before payment |
| 422 | Verification rejected | Do not assume a charge; read original order and reconcile |
| 429 / 503 | Rate bound / launch held, provider, daily write allocation or storage unavailable | Read existing orders; do not create a replacement paid job |

Errors are JSON `{ "schema_version": "lumen-interface-paid-error/1", "error": "stable_code" }`. Examples include `interface_check_disabled`, `malformed_json`, `body_too_large`, `unsupported_content_type`, `private_recovery_key_required`, `invalid_submission`, `unsupported_openapi`, `result_too_large`, `recovery_key_conflict`, `not_authorized`, `quote_expired` and `purchased_report_unavailable`. Provider-binding error codes identify the rejected binding, without returning credentials. Durable review states carry sanitized reasons; HTTP success alone is not payment confirmation.

## Runnable private client

Node.js 22 or later is required. The standalone client uses only Node built-ins for preparation/recovery and dynamically imports pinned payment packages for its helper adapters (`mppx@0.13.4`, `@x402/core@2.28.0`). Obtain the public client and example input from the offer. Choose a new absolute state directory whose parent already exists. All commands print sanitized status JSON and never print capability, proof or submitted documents.

```sh
node interface-agent-client.mjs prepare --state /absolute/private/check-job --input example-input.json --method x402 --identity base:YOUR_BUYER_ADDRESS --recipient VERIFIED_MERCHANT_ADDRESS
node interface-agent-client.mjs recover --state /absolute/private/check-job --method x402 --identity base:YOUR_BUYER_ADDRESS --recipient VERIFIED_MERCHANT_ADDRESS
node interface-agent-client.mjs refresh --state /absolute/private/check-job --method x402 --identity base:YOUR_BUYER_ADDRESS --recipient VERIFIED_MERCHANT_ADDRESS
node interface-agent-client.mjs pay --state /absolute/private/check-job --method x402 --identity base:YOUR_BUYER_ADDRESS --recipient VERIFIED_MERCHANT_ADDRESS --adapter /absolute/private/buyer-adapter.mjs
```

`pay` is the only financial dispatch command; invoke it only with the owner's exact one-purchase authority. It rechecks payer identity and fresh order/quote binding, commits its permanent dispatch marker, creates one proof and sends one financial POST. It then recovers using reads. Repeating `pay` after a marker exists only recovers; it never invokes the adapter again. If the process dies before or during preparation, `recover` can locate and persist a returned order using the saved capability even when the original order ID was lost. A lock or payment marker becomes visible only after its complete private record is fsynced; an exclusive hard link publishes it, followed by directory fsync before protected work begins. A crash before publication can leave an inert temporary file, while a crash after publication leaves a complete record. Dead-process local locks can be reclaimed; a living process lock blocks a second writer. Empty, truncated or otherwise corrupt inherited locks fail closed because the client cannot safely determine their owner; it preserves them for manual local repair and never treats corruption as payment-retry authority. The guarantees assume a trusted same-UID filesystem with hard-link support and durable fsync semantics; back up the private directory. An adapter failure after marker creation remains read/reconcile-only and may require manual resolution rather than a fresh payment.

Your private adapter module exports `async function createAdapter()` returning `{ readIdentity, createProof }`. Creating the adapter must perform no signing, token issuance or financial call. `readIdentity()` returns exactly the safe payer identity string saved with this job; Base identities use a lowercase address (`base:0x...`). `createProof({payment_method,order,quote})` returns a single header credential string; it must not submit an HTTP payment or retry a payment itself. Keep credentials in your own custody integration, never in public client source.

For Base EIP-3009 custody with a viem-compatible signer, the client supplies a runnable helper that uses x402 core and signs the exact **order-bound nonce**. Generic x402 exact clients using a random nonce will be rejected:

```js
import {createX402ExactAdapter} from './interface-agent-client.mjs';
import {getApprovedCustodySigner} from './your-private-custody.mjs';
export async function createAdapter() {
  const signer = await getApprovedCustodySigner(); // {address, signTypedData}; no key export
  return createX402ExactAdapter({signer, readIdentity: async () => 'base:' + signer.address.toLowerCase()});
}
```

The generic `createX402Adapter({client,paymentIdentity,readIdentity})` helper accepts an externally configured x402 core client and verifies that its proof used the advertised bound nonce. It does not wrap fetch or auto-pay retries. Checker nonces reserve the signed prefix `0x4c554d454e494331` (LUMENIC1), followed by a 192-bit digest derived from the exact purchase binding. The combined Worker rejects this signed nonce namespace on Manifest Kit's existing x402 route, including a proof whose unsigned HTTP resource fields were replaced. Any production rollback after checker activation must preserve that guard and both products' private order state.

For Stripe, set `--method stripe --identity YOUR_ISSUER_ACCOUNT_ALIAS --stripe-network-id VERIFIED_STRIPE_MERCHANT_PROFILE_ID`. A private adapter can return `await createStripeMppAdapter({paymentIdentity, paymentMethod, createToken, readIdentity})`, where `paymentMethod` is the caller's authorized Stripe payment-method ID and `createToken` is their approved issuer's programmatic Shared Payment Token operation. The pinned mppx helper generates the Payment credential for the saved challenge; it uses `externalId = order_id` and exact product/snapshot/report metadata. The merchant does not provide buyer card issuance or accept raw card numbers. No issuer, wallet authentication or new credentials are configured by this candidate.

Local simulated verification may explicitly set `--origin http://127.0.0.1:PORT --loopback true`; this transport option does not change quoted canonical merchant URLs or grant live authority. Payment validation still pins the canonical quote resource. No automatic redirect is followed.

## Privacy and discovery

Submit public interface documents only; never submit keys, tokens, personal records or customer credentials. Raw input is discarded from the server after report preparation; the private journal retains hashes, the saved report and sanitized payment/reconciliation metadata. Findings may reveal supplied field structure and suggested values, so treat the result as private. The client removes raw input from its journal after successful preparation, while the caller's original input file remains under caller control.

Capabilities, proofs and reports never belong in a URL, public static asset, shared cache, analytics, email or log. The API responds private/no-store; the static documentation and fictional examples are public. Cloudflare and payment providers may retain their own service/payment records. Base payments expose transaction addresses and amounts publicly on-chain.

`bazaar.json` is prepared x402 discovery metadata. It does **not** establish current Bazaar indexing, a successful purchase or live availability. Check availability flags in the current offer/catalog/OpenAPI before authorizing payment. Existing Manifest Kit checkouts and private downloads retain their own contracts. Support Ben remains an optional personal gift through its separate hosted Buy Me a Coffee checkout and purchases no checker entitlement.
