ChangeGamer

← All resources

Paying for Access: The HTTP 402 Flow

Policy · updated 2026-07-08 · Markdown variant

How autonomous agents encounter, interpret and satisfy HTTP 402 Payment Required responses on ChangeGamer.


HTTP 402 Payment Required is the standard status code for paywalled resources. This guide explains exactly how ChangeGamer uses it — what a 402 response looks like, how to buy an access key, how to retry, and which paths can never return 402.

TL;DR for agents

  1. You hit 402 on a premium slug. The JSON body gives you payment_url, price_usd, and a preview (title, description, and section outline) of what is behind the gate.
  2. Buy a key at payment_url (€5 one-time starter). Fulfillment is self-serve: Stripe redirects to /key?session_id=..., which returns your cg_ key as JSON.
  3. Retry the original request with Authorization: Bearer <key> (or x-api-key: <key>). The key is valid until revoked and unlocks the whole premium set.

Always free, never 402: /llms.txt, /llms-full.txt, robots.txt, the sitemap, /license.xml, and the getting-started + access-and-pricing resources.

Which paths can return 402

Only three URL shapes are eligible to return 402, controlled by SLUG_PATTERNS in the worker and the run_worker_first list in wrangler.jsonc:

The following paths are asset-served (they bypass the worker entirely) and can never be paywalled by code:

/api/resources.json (the corpus index) matches no slug pattern in the worker, so it also cannot return 402.

Additionally, two slugs are hard-coded as FREE_SLUGS in the worker — getting-started and access-and-pricing — which means adding them to the premium list has no effect; they always pass through for free.

Current status — the gate is active

The premium set currently contains Data Formats & Schema, JSON API for Agents, and How ChangeGamer Runs Itself; requests to those slugs without a valid key return 402. All other resources are free. The authoritative, always-current premium list is announced on the access-and-pricing resource whenever it changes.

Exact 402 response contract

When a request hits a premium slug without a valid key, the worker returns:

HTTP/1.1 402 Payment Required
Content-Type: application/json; charset=utf-8
Link: <https://buy.stripe.com/fZu9AMa230eP2b1f0d7EQ00>; rel="payment", <https://changegamer.ai/license.xml>; rel="license", <https://changegamer.ai/api/pricing.json>; rel="pricing"
Cache-Control: no-store

{
  "error": "payment_required",
  "resource": "<slug>",
  "price_usd": "0.05",
  "payment_url": "https://buy.stripe.com/fZu9AMa230eP2b1f0d7EQ00",
  "how_to_pay": "Buy an access key at payment_url, then retry with header 'Authorization: Bearer <key>'. Index files (/llms.txt) and the getting-started and access-and-pricing resources are always free.",
  "terms": "https://changegamer.ai/resources/access-and-pricing.md",
  "license": "https://changegamer.ai/license.xml",
  "pricing_catalog": "https://changegamer.ai/api/pricing.json",
  "preview": {
    "title": "<resource title>",
    "description": "<one-line resource summary>",
    "sections": ["<## heading>", "..."]
  }
}

The JSON keys are, in order: error, resource, price_usd, payment_url, how_to_pay, terms, license, pricing_catalog, preview. The preview object holds the resource title, one-line description, and its ## section-heading outline (no paywalled body prose).

Note on price_usd: this field represents the per-fetch reference price in USD used by the worker (currently "0.05"). The access key itself is sold in EUR — a €5.00 one-time starter key. The two figures are distinct: price_usd is the per-request accounting unit; €5 is the key purchase price. The field name is not a bug; do not treat them as the same currency.

Purchasing an access key

A starter key costs €5.00 (one-time, EUR). Purchase at:

https://buy.stripe.com/fZu9AMa230eP2b1f0d7EQ00

Fulfillment is self-serve and automatic:

  1. Pay at https://buy.stripe.com/fZu9AMa230eP2b1f0d7EQ00
  2. Stripe redirects you to https://changegamer.ai/key?session_id={CHECKOUT_SESSION_ID}
  3. That URL returns JSON containing your cg_ access key.

Email fallback: reply to your Stripe receipt email if the self-serve flow does not resolve.

GET /key — self-serve key delivery contract

Endpoint: GET https://changegamer.ai/key?session_id=<cs_live_...>

All responses carry Cache-Control: no-store (worker/index.ts lines 357, 371, 382, 390, 405, 433). The session_id must match cs_(live|test)_[A-Za-z0-9]+ and be ≤200 chars; otherwise the endpoint returns 400.

200 — key ready (worker/index.ts lines 421–435):

{
  "key": "cg_<40 hex chars>",
  "created": "<ISO-8601 timestamp>",
  "tier": "starter",
  "usage": {
    "header_bearer": "Authorization: Bearer cg_...",
    "header_api_key": "x-api-key: cg_...",
    "instructions": "https://changegamer.ai/resources/paying-for-access-402.md"
  }
}

404 — not found yet (worker/index.ts lines 395–407): {"error":"not_found", "message":"..."}. The webhook delivery may lag the payment redirect by a few seconds — wait a moment and retry. The session→key mapping is retained for 30 days (expirationTtl: 2592000, worker/index.ts line 325).

400 — bad or missing session_id (worker/index.ts lines 366–385): missing session_id parameter, or value that fails the format check.

Do not cache any /key response. The webhook endpoint POST https://changegamer.ai/webhooks/stripe is Stripe-facing; its internals are not documented here beyond its existence.

Retrying with a key

The worker checks for a key in this order (first match wins):

  1. Authorization: Bearer <key> header — preferred. The scheme check is case-insensitive; the key is trimmed of whitespace.
  2. x-api-key: <key> header — accepted as a fallback.

Example retry:

GET /resources/some-premium-slug HTTP/1.1
Host: changegamer.ai
Authorization: Bearer cg_your_key_here

Or alternatively:

GET /resources/some-premium-slug HTTP/1.1
Host: changegamer.ai
x-api-key: cg_your_key_here

A valid key causes the worker to fall through to static asset delivery — the resource is then served normally with its standard HTTP 200 response.

Summary for agent implementors

Native x402 (keyless, on-chain) — buyer walkthrough

A native x402 endpoint exists at /api/x402/<slug> for fully autonomous, keyless, on-chain (USDC) payment — no account, no key purchase. It is dormant today: every request currently returns HTTP 503 x402_not_configured, because none of the five operator env vars it requires (X402_FACILITATOR_URL, X402_PAY_TO, X402_NETWORK, X402_ASSET, X402_MAX_AMOUNT) are set on this server. The Bearer-key flow documented above is the live rail. /api/payment.json is the source of truth for the current status of every payment method — check it before attempting a native x402 call. For the general x402 buyer loop and client libraries (not specific to this site), see agent wallets; this section covers only this endpoint's own request/response contract.

Today's actual behavior — an exact curl call against the live endpoint

This works right now and returns 503, not a payment prompt (example slug data-formats, currently premium):

$ curl -sS -i https://changegamer.ai/api/x402/data-formats

HTTP/1.1 503 Service Unavailable
Content-Type: application/json; charset=utf-8

{
  "error": "x402_not_configured",
  "message": "Native x402 on-chain settlement is not yet enabled on this server. Use the HTTP 402 + Bearer key flow or Stripe checkout instead.",
  "fallback": {
    "http_402": "https://changegamer.ai/resources/data-formats.md",
    "payment_manifest": "https://changegamer.ai/api/payment.json",
    "pricing_catalog": "https://changegamer.ai/api/pricing.json"
  }
}

(Calling a non-premium slug instead returns 400 resource_is_free; an unknown slug returns 404; a non-GET method returns 405 with an Allow: GET header. Those checks run first and short-circuit before the config check; for any request that passes them — a valid premium slug, GET method — the 503 above fires next, whether or not an X-PAYMENT header is sent.)

The documented future contract — NOT live, for when a facilitator + wallet are configured

Everything in this subsection is a specification of what the code is built to do once the operator sets all five env vars above. None are set today; none of the requests below currently work. Treat every curl command and response here as hypothetical, not a claim about current site behavior.

Step 1 — discovery. The same call as above, once configured, would draw a 402 instead of a 503, carrying the payment requirements in accepts[0]:

# hypothetical — endpoint is dormant today
$ curl -sS -i https://changegamer.ai/api/x402/data-formats

HTTP/1.1 402 Payment Required
Content-Type: application/json; charset=utf-8

{
  "x402Version": 1,
  "error": "X-PAYMENT header required",
  "accepts": [
    {
      "scheme": "exact",
      "network": "<operator-configured, e.g. base>",
      "maxAmountRequired": "<operator-configured>",
      "resource": "https://changegamer.ai/api/x402/data-formats",
      "description": "ChangeGamer premium resource: data-formats",
      "mimeType": "text/markdown",
      "payTo": "<operator-configured, e.g. a Base wallet address>",
      "maxTimeoutSeconds": 60,
      "asset": "<operator-configured, e.g. a USDC contract address>"
    }
  ]
}

Field order in the accepts[0] object mirrors the worker's requirements builder exactly; it may also carry an optional trailing extra: { name, version } object if the operator sets an asset name (e.g. for EIP-712 domain data). A malformed retry — an X-PAYMENT header that is not valid base64-encoded JSON — would draw 400 invalid_x_payment_header with the same accepts array repeated.

Step 2 — pay and retry. A client library (see agent wallets for which ones) signs a payment authorization against the accepts[0] requirements, base64-encodes the signed payload, and retries with an X-PAYMENT header:

# hypothetical — endpoint is dormant today
$ curl -sS -i https://changegamer.ai/api/x402/data-formats \
    -H "X-PAYMENT: <base64-encoded signed PaymentPayload JSON>"

HTTP/1.1 200 OK
Content-Type: text/markdown; charset=utf-8
X-PAYMENT-RESPONSE: <base64-encoded settlement receipt JSON>

# Data Formats & Schema

> Structured data conventions: JSON-LD, Markdown variants and stable slugs.

...full resource Markdown body...

That 200 only happens once the worker's two facilitator calls (POST <facilitator>/verify, then POST <facilitator>/settle) both succeed. Other outcomes the code defines: the facilitator rejects the payment at /verify or /settle402 with error set to its invalidReason (falling back to payment_invalid) or errorReason (falling back to settlement_failed); the worker fails to sign its own facilitator-auth request → 502 facilitator_auth_failed; the facilitator is unreachable or returns something unparseable → 502 facilitator_unreachable.

Once again: nothing in this future-contract subsection is reachable today. Confirm the live status of /api/x402/<slug> at /api/payment.json — while its x402 entry there confirms the same dormant/503 state, the only working call against that path is the curl example in "Today's actual behavior" above, and it always returns 503.

#402 #payments #api-keys #monetization

Category: Policy

Like this? See pricing for the full corpus license, or preview the exact format free as NDJSON or JSON.