Reference · Gateway

Gateway HTTP spec

What a buyer (or their agent) sends, what the gateway sends back, and exactly what every error code means. For the seller-side setup, see /app.

URL

https://www.lemoncake.xyz/g/<shortId>

shortIdis the 8-character base32 ID printed in the seller's Gateway pane on /app. The gateway is method-agnostic — GET / POST / PUT / PATCH / DELETEare all forwarded to the seller's origin URL with the same method and body.

Authentication

Every paid request must carry a Pay Token JWT:

POST https://www.lemoncake.xyz/g/abc12345 HTTP/1.1
Host: www.lemoncake.xyz
Authorization: Bearer eyJhbGciOiJIUzI1NiI…
Content-Type: application/json

{ "query": "your upstream payload" }

JWTs are HS256-signed. The gateway verifies the signature, looks up the token row in Postgres, and rejects on revoked / expired / exhausted / over-budget. See Pay Token spec for the full payload format.

Successful response

The upstream's body and most headers are streamed back verbatim (hop-by-hop headers are stripped). Two LemonCake trace headers are added so buyers can audit charges:

HTTP/2 200 OK
content-type: application/json
x-lemoncake-charge: 0.01
x-lemoncake-upstream-ms: 17

{ "result": "…" }
HeaderMeaning
x-lemoncake-chargeUSD amount debited from your Pay Token's budget for this call.
x-lemoncake-upstream-msTime the upstream took to respond. Gateway-side overhead is ~10ms (Tokyo region).

Error responses

All errors return a JSON body of { "error": "<code>" }. No Pay Token charge is recorded on any non-2xx/3xx outcome.

Statuserror codeMeaning
401missing_pay_tokenNo Authorization header / wrong scheme. Attach `Authorization: Bearer <jwt>`.
401invalid_pay_tokenJWT signature or claims invalid. Likely tampered or signed with a different LC_JWT_SECRET.
401token_expiredThe Pay Token's expiry has passed. Ask the seller to issue a fresh one.
402spend_cap_exceededPay Token's budget would go negative on this call. Request a top-up.
402token_exhaustedPay Token has hit its max_calls cap.
403token_revokedSeller manually revoked this Pay Token.
403token_endpoint_mismatchJWT was issued for a different endpoint than the one you're calling.
404endpoint_not_foundshortId doesn't exist (typo, or the seller deleted it).
429rate_limit_exceededMore than `rate_limit` successful calls in the last 60s for this endpoint.
503endpoint_pausedSeller temporarily paused this endpoint. Try again later.
502upstream_unreachableGateway couldn't reach the seller's origin. Pay Token is NOT debited.
503backend_not_configuredGateway env vars missing on the LemonCake side. Email contact@aievid.com.

Refund semantics

The gateway only charges your Pay Token when the upstream returns 2xx / 3xx / 4xx. On 5xx or network failure, the call is logged in the blocked log under upstream_error and your token is notdebited. Buyers never pay for the seller's origin outage.

CORS

The gateway returns permissive CORS so browser-side agents work out of the box:

Access-Control-Allow-Origin: <Origin echo>
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Expose-Headers: x-lemoncake-charge, x-lemoncake-upstream-ms
Access-Control-Max-Age: 86400

Preflight OPTIONS returns 204 without authentication or DB lookups.

// Browser-side example
const res = await fetch("https://www.lemoncake.xyz/g/abc12345", {
  method: "POST",
  headers: { "Authorization": "Bearer " + PAY_TOKEN_JWT },
  body: JSON.stringify({ query: "…" }),
});
console.log(res.headers.get("x-lemoncake-charge")); // "0.01"

Header forwarding rules

The gateway forwards almost all request headers to the upstream, with these exceptions:

BehaviourHeaders
Stripped (hop-by-hop)host, connection, content-length, transfer-encoding, keep-alive, proxy-authenticate, proxy-authorization, te, trailer, upgrade
ReplacedAuthorization — your Pay Token JWT is replaced with the seller's stored upstream_auth header (if any)
Never forwardedCookie — buyer-side cookies are not leaked to the seller's origin

Charging math

For every successful call:

gross = endpoint.price_per_call
fee   = (in the seller's first 3,000 calls / UTC month)  → 0
        (after 3,000)                                    → gross * 0.03
net   = gross - fee

The buyer's Pay Token decrements by grossregardless of free-tier state — the free tier affects LemonCake's cut, not the buyer's spend. Per-call breakdown is logged in lc_test_runs, visible on the seller's Usage Ledger pane.

Next