1---
2title: 2.0.0-beta.55 - AI Gateway Spend Caps
3date: 2026-06-11T20:00:00Z
4category: release
5excerpt: v2.0.0-beta.55 adds `Cloudflare.AiGatewaySpendingLimit` — a declarative hard cap on account-wide AI Gateway spend — and re-exports the fetcher interop utils from `alchemy/Cloudflare/Bridge` so framework workers can wrap a service binding in a typed `HttpClient`.
6---
7
8An AI Gateway sits in front of your model calls, which means it's also
9where a runaway loop turns into a runaway bill. beta.55 puts the
10guardrail in your stack: **`Cloudflare.AiGatewaySpendingLimit`** sets a
11hard dollar cap on cumulative AI Gateway spend across every gateway in
12the account, declared and reconciled like any other resource.
13
14## `Cloudflare.AiGatewaySpendingLimit`
15
16Amounts are in **cents** (Cloudflare's native unit — minimum `1_00` =
17$1.00), accumulated over a `daily`, `weekly`, or `monthly` window with a
18`fixed` (reset on the boundary) or `sliding` (rolling window) strategy:
19
20```typescript
21import * as Cloudflare from "alchemy/Cloudflare";
22
23// Cloudflare requires one manual credit top-up before a spending limit
24// can be set. `topUp` reconciles that requirement: the provider observes
25// the billing state and only charges the account's default payment
26// method if the account has never topped up.
27const cap = yield* Cloudflare.AiGatewaySpendingLimit("ai-spend-cap", {
28 amount: 250_00, // cents -> $250.00 (minimum 1_00 = $1.00)
29 duration: "monthly",
30 topUp: { amount: 10_00 }, // cents -> $10.00 (Cloudflare minimum)
31});
32```
33
34The `topUp` prop deals with Cloudflare's bootstrap requirement:
35an account can't set a spending limit until it has loaded Unified
36Billing credits via at least one manual top-up (`NO_MANUAL_TOPUP`).
37The provider observes `first_topup_success` on the live billing API
38and only makes the one-time charge when the account has never topped
39up — once bootstrapped, the prop is inert and never charges again. If
40the payment needs interactive confirmation (3-D Secure), the resource
41fails with a typed `AiGatewaySpendingLimitTopupRequired` error and the
42top-up must be completed in the dashboard.
43
44Auto recharge is on by default — when the credit balance drops below
45`threshold`, Cloudflare recharges by `amount` automatically:
46
47```typescript
48const cap = yield* Cloudflare.AiGatewaySpendingLimit("ai-spend-cap", {
49 amount: 250_00,
50 duration: "monthly",
51 topUp: { amount: 20_00, threshold: 10_00 }, // recharge $20 below $10
52});
53```
54
55Set `topUp: { amount: 10_00, autoRecharge: false }` for a one-time
56bootstrap only.
57
58One caveat: Cloudflare stores a **single limit per account**, so this
59resource is a per-account singleton — declare exactly one. Thanks
60**[Matthew Aylward](https://github.com/Butch78)**
61([#569](https://github.com/alchemy-run/alchemy/pull/569)).
62
63## Fetcher utils on `alchemy/Cloudflare/Bridge`
64
65The fetcher/socket interop helpers — `fromCloudflareFetcher`,
66`toCloudflareFetcher`, `toHttpClient`, `fromCloudflareSocket` — are now
67exported from the `alchemy/Cloudflare/Bridge` entrypoint, and
68`fromCloudflareFetcher` / `fromCloudflareSocket` accept the global
69`Fetcher` / `Socket` types directly
70([#581](https://github.com/alchemy-run/alchemy/pull/581)).
71
72That combination matters inside a framework worker (TanStack Start,
73Astro, …), where your Effect backend arrives as a plain service binding
74on `env`. Two calls turn it into a fully typed Effect `HttpClient` —
75no casts:
76
77```typescript
78import * as Cloudflare from "alchemy/Cloudflare/Bridge";
79
80const client = Cloudflare.toHttpClient(
81 Cloudflare.fromCloudflareFetcher(env.BACKEND),
82);
83const res = await client
84 .get(`https://backend/?key=${encodeURIComponent(key)}`)
85 .pipe(Effect.runPromise);
86return HttpServerResponse.toWeb(
87 HttpServerResponse.fromClientResponse(res),
88);
89```
90
91The request rides the in-account service binding, not the public
92network. The `cloudflare-website-tanstack-start` example's `/api/hello` route now
93serves the same object four ways side by side — the raw R2 binding,
94the service binding's `fetch`, typed RPC (`toRpcAsync`), and this
95`HttpClient` form — each covered by an integration test.
96
97## Also in this release
98
99- **Fixed a dangling process that kept `alchemy deploy` / `alchemy destroy`
100 from exiting** — local `RpcProvider` service layers (`providerServices` /
101 `providerServicesEffect`) now only construct when `AlchemyContext.dev`
102 is set, so one-shot CLI runs no longer spin up the local dev provider
103 machinery ([#580](https://github.com/alchemy-run/alchemy/pull/580))
104
105## Where to go next
106
107- [Add an AI Gateway](/cloudflare/ai/ai-gateway)
108- [Frontend frameworks](/cloudflare/frontend/frontends)
109- [CHANGELOG](https://github.com/alchemy-run/alchemy/blob/main/CHANGELOG.md#v200-beta55)
110- [Compare v2.0.0-beta.54 → v2.0.0-beta.55](https://github.com/alchemy-run/alchemy/compare/v2.0.0-beta.54...v2.0.0-beta.55)