Claude for business on an Enterprise plan now runs on a shared budget per team, and this post shows how to test a budget before the month starts, so that by the end you can do it on your own team's numbers: build a spend history in the shape of Anthropic's cost export, replay next month day by day under the help article's rules, ask Claude for a budget through one tool, score every proposal by who it stops and when, and recompute all of it with a second replay that shares no logic with the first. Everything below comes from the real files, and every code block is trimmed from them. The trigger was two releases a day apart. On 28 October Anthropic made [pooled group budgets generally available on Claude Enterprise](https://claude.com/resources/articles/smart-reports-surveys-pooled-budgets): one shared monthly amount per group, plus a member limit that is the same for everyone in it. On 29 October the [Usage and Cost Analytics API](https://platform.claude.com/docs/en/manage-claude/usage-cost-analytics-api) went into beta, exporting cost per person per day. Together they turn "what should our team's Claude budget be" into a question you can test. I had Claude Code build the test, and a GitHub workflow made the API calls.  ## What are the rules of a Claude for business shared budget? Two numbers, both ceilings. Every request counts against the person's member limit and against the group's shared amount, and the person stops when the first of the two runs out. The rest comes from [the help article on pooled group budgets](https://support.claude.com/en/articles/17005973-manage-pooled-group-budgets-on-enterprise-plans), read on 3 November. Billing admins get an email at 50, 75, 95 and 100 percent of the shared amount. A stopped member can press "Request more", which goes to an admin. The article suggests a starting point, a shared amount of about three times last month's group spend and a member limit of about three times last month's top spender. It also warns: "If you set the member monthly limit to the pooled budget divided by the number of group members, you're back to one-person limits and lose the benefit of a shared budget." Those rules are the whole replay. Everything else in the test is invented. ## How do you fake an export you can trust? Seed it, and build in the things a real export has: holidays, people who are off, a month-end spike, a hire with no history. Then pin the shape to the API's own response, so the code you test against is the code you would run against real data. pack.py\ writes pack.json\ from seed 20261103. The company is Fjordlys Software ApS, invented, with a commercial team of 15: a sales director, seven account executives, two SDRs, a bid manager, a RevOps analyst, marketing, content and customer success. Each person has a base dollar rate per working day, with lognormal noise and about 6 percent of days with no use. August has two summer-holiday weeks with nine of the team away. The RevOps analyst triples her use over the last six working days of every month. November adds the two things history cannot show: a public tender from 9 to 20 November, due Friday the 20th at 12:00, and a new account executive from Monday 9 November. Trimmed from pack.py\: \python SEED = 20261103 TENDER = (date(2026, 11, 9), date(2026, 11, 20)) TENDER\_MULT = {"anders": 5.5, "line": 3.0, "rasmus": 3.0} CLOSE\_MULT = {"nanna": 3.0} # month-end close, last 6 workdays of every month HOLIDAY = (date(2026, 8, 3), date(2026, 8, 14)) # Danish summer holiday, most of the team away # ... inside the day loop, per person rate = NEW\_HIRE\_RATE if pid == NEW\_HIRE else base mult = 1.0 if TENDER\[0\] <= d <= TENDER\[1\] and pid in TENDER\_MULT: mult \*= TENDER\_MULT\[pid\] if d in close and pid in CLOSE\_MULT: mult \*= CLOSE\_MULT\[pid\] if rng.random() < 0.06: # a day off, a day of meetings continue noise = math.exp(rng.gauss(0, 0.35)) rows.append((d, pid, round(rate \* mult \* noise, 2))) \\ The pack also keeps three rows in the exact shape of the cost endpoint grouped by actor in one-day buckets: starting\_at\, ending\_at\, a group.actor\ with user\_id\ and email\, and cost.amount\ as a decimal string in cents with six decimals, currency always USD. What the job sends Claude is those rows flattened to dollars as a CSV, date\_utc,user\_email,cost\_usd\, which is what an admin would paste into a chat. For August to October that came to 31,115 characters. The group totals: August $1,545.55, September $1,948.15, October $1,974.07. November's demand, meaning what everyone would spend if nothing stopped them, is $2,541.92. check()\ refuses a pack where the new hire has history, where history leaks into November, or where November does not have 21 working days. ## How does the replay apply the help article's rules? Day by day, in November order. For each person the replay caps the day's demand at what is left of their member limit, then caps the day's total at what is left of the shared amount, and records anyone who got less than they asked for. One rule needed a decision that the help article does not make for you: when the shared amount cannot cover a whole day, who gets served? replay.py\ shares what is left in proportion to demand. The other simplification is stated in the docstring and wherever a number is quoted: a stopped member stays stopped until the month ends. In the product they can press "Request more", so the replay is the case where nobody answers. Trimmed from replay.py\: \python for d in sorted(by\_day): want = {} for pid, usd in by\_day\[d\].items(): room = float("inf") if member\_limit is None else max(0.0, member\_limit - used\[pid\]) want\[pid\] = (usd, min(usd, room)) total\_ok = sum(v\[1\] for v in want.values()) pool\_left = float("inf") if pool is None else max(0.0, pool - pool\_used) scale = 1.0 if total\_ok <= pool\_left else (pool\_left / total\_ok if total\_ok else 0.0) for pid, (usd, ok) in want.items(): got = ok \* scale used\[pid\] += got pool\_used += got miss = usd - got if miss > 0.005: unmet\[pid\] += miss if d in workdays: short\_days\[pid\] += 1 stopped\_on.setdefault(pid, d) if pool: for a in ALERTS: if a not in alerts and pool\_used >= pool \* a - 0.005: alerts\[a\] = d \\ Two reference budgets run through it with no Claude involved. split\ is the help article's warned-against case: Anthropic's starting amount divided by 15, so a member limit of $394.81 and no shared amount. The bid manager stops on 20 November, deadline day, and is short for 7 working days. start\ is Anthropic's starting point from October: $5,922.21 shared and $1,225.05 per member. Nobody stops, November uses 43 percent, and no alert fires.  ## How was Claude asked for a budget? Through one tool, submit\_budget\, with no forced tool\_choice\. The model gets the export, the rules and a request from the sales director, and submits two numbers plus a reason in Danish and in English. The request is written in Danish the way a sales director would write it: the team moves to a shared budget on 1 November, finance wants two numbers, and she would rather spend a little too much than stop someone in the middle of something important. The mechanics are paraphrased from the help article. The three-times rule of thumb is left out on purpose, so the proposal is Claude's own. Half the runs also get a calendar note naming the tender team, the deadline, the new hire and the month-end close. Four conditions, six runs each, pre-registered in BUILD.md\ before the job ran: | Condition | Model | Calendar note | |---|---|---| | note-sonnet\ | claude-sonnet-5-5\ | yes | | bare-sonnet\ | claude-sonnet-5-5\ | no | | note-haiku\ | claude-haiku-5-5\ | yes | | bare-haiku\ | claude-haiku-5-5\ | no | The tool is not forced because Sonnet 5.5 refuses a forced tool call; the previous build in this series learned that the hard way. So the job allows up to two nudges. Trimmed from api\_job.py\: \python NUDGE = "Indsend venligst dit forslag med submit\_budget." for \_turn in range(MAX\_TURNS): msg = call(model, messages, rec) # max\_tokens=6000, tools=\[TOOL\] for b in msg.content: if b.type == "text" and b.text: rec\["texts"\].append(b.text) if b.type == "tool\_use" and b.name == "submit\_budget": rec\["budget"\] = dict(b.input) if rec\["budget"\] is not None: break messages.append({"role": "assistant", "content": list(msg.content)}) messages.append({"role": "user", "content": NUDGE}) rec\["nudged"\] = rec.get("nudged", 0) + 1 if rec\["budget"\] is None: rec\["error"\] = "no submit\_budget in the reply" \\ The job runs only inside the lab-api\ GitHub workflow, which authenticates with workload identity federation, so anthropic.Anthropic()\ picks up its identity there and no API key exists anywhere. A dry run with BUDGET\_FAKE=1\ and a fake client exercised the job, the scorer and the replay before a real call was spent: 24 submitted, 0 errors. ## What did the run actually record? 24 budgets, 0 errors, 49 calls. No run submitted on its first call: the first call in every run went to the 6,000-token output limit and stopped on max\_tokens\ without calling the tool, the nudge then got a submission, and one run needed the second nudge. That is 24 times two, plus one. The run was lab-api run 41722729902, on 3 November 2026 from 21:42:48 to 21:46:05 UTC, SDK 2.4.0. Tokens: 1,098,783 in, 213,385 out. Cost at list price: $2.21, of which Sonnet $2.10 and Haiku $0.11. One run, bare-haiku\ 3, hit the output limit on its second call as well; its tool call carried the two numbers but no reason text. It is counted, and the demo says so. If you reuse this harness, raise max\_tokens\ or ask for the tool call earlier in the prompt, and log stop\_reason\ per call the way this job does, because otherwise "it needed a nudge" hides "it ran out of room". ## How were the proposals scored? score.py\ replays November through every submitted budget and reports, per run and per condition: did it stop nobody, did it stop the bid manager on or before 20 November, did the shared amount run out, the ceiling as a multiple of November's demand, working days short, and cost. | Condition | Stopped nobody | Bid manager stopped by the deadline | Shared amount ran out | Median shared | Median member limit | Median ceiling / demand | |---|---|---|---|---|---|---| | note-sonnet\ | 6 of 6 | 0 | 0 | 3,000 | 600 | 1.18 | | bare-sonnet\ | 5 of 6 | 0 | 1 | 2,700 | 575 | 1.06 | | note-haiku\ | 0 of 6 | 5 | 1 | 2,500 | 350 | 0.98 | | bare-haiku\ | 1 of 6 | 1 | 5 | 2,450 | 550 | 0.96 |  Sonnet with the calendar built the budget from September and October, set August aside because of the holiday, added the new seller and the tender, put about 20 percent on top, and set the member limit from the RevOps analyst's busiest month. Five of six runs landed on exactly $3,000 and $600, the sixth on $3,500 and $600. That ceiling is about half of Anthropic's starting point, and nobody was stopped. Haiku with the calendar read the tender too, and in five of six runs set the member limit between $250 and $400, below the $454.15 the bid manager needed in November. Four of its six reasons left the RevOps analyst out of their sums or doubted she was in the group. The scorer's per-condition numbers make the failure easy to locate: Haiku's median shared amount was close to Sonnet's, and its median member limit was not. On a pooled budget the member limit is the number to test hardest. ## Why a second replay, and how do you know it works? Because a scorer that shares code with the thing it scores can be wrong in the same way twice. verify.py\ re-derives every number the campaign quotes with a separately written replay, and it was negative-tested, so it is known to fail on a wrong result as well as to pass on a right one. It does four things. It rebuilds the pack from the seed and compares it with pack.json\. It recomputes Anthropic's two reference budgets from the October export and checks them against what the scorer used. It replays every reference budget and every submitted budget with replay2()\, a plainer walk over the days with two counters, and compares who stopped, on which day, working days short and dollars short with score.py\. And it checks that every submitted budget in the raw runs is numeric. Any disagreement exits non-zero. Only then does it write facts.json\, the one file the demo, the film and the article read their numbers from, so nothing downstream types a number by hand. ## What does the replay leave out? "Request more". A stopped person can press it, and a quick admin would have rescued most of these stops. In the replay nobody answers, so every stop lasts the month. That is the case to plan for, and it is stricter than the product. The company, the people and the spend are invented; only Anthropic's rules are real. Anthropic publishes no per-person price, so what Claude for business costs your team is in your own export. ## Try it on your own export Pull three months from the cost endpoint, drop months that do not look like the one you are budgeting, write down what is different next month, and replay it against the two numbers you are about to send. If you ask Claude to propose them, use the larger model, give it the calendar, and ask it to name the person the member limit was set from. There is also [a short film of the replay](https://brinvik.com/claude-news/2026-11-03-claude-faelles-budget/brinvik-budget-film-en-1080x1080.mp4?utm_source=devto&utm_medium=article&utm_campaign=2026-11-03-claude-faelles-budget&utm_content=devto-film):  The full write-up, with an interactive demo where you can replay November under each budget and click through all 24 of Claude's proposals and reasons, is [the English article on a shared Claude budget for a sales team](https://brinvik.com/en/journal/claude-enterprise-shared-budget-sales-team?utm_source=devto&utm_medium=article&utm_campaign=2026-11-03-claude-faelles-budget&utm_content=devto-en), and there's [a Danish version of the article about the team budget](https://brinvik.com/da/journal/claude-enterprise-faelles-budget-salgsteam?utm_source=devto&utm_medium=article&utm_campaign=2026-11-03-claude-faelles-budget&utm_content=devto-da). I'm Kim Olsen, a one-person AI consultant in Copenhagen. When a team moves to a shared Claude budget, I help set it up so the money follows the work: who goes in which group, which numbers the budget starts on, and who answers "Request more" on a Friday. That's part of my [AI advisory work on rolling out Claude](https://brinvik.com/en/services/ai-advisory?utm_source=devto&utm_medium=article&utm_campaign=2026-11-03-claude-faelles-budget&utm_content=devto-service).
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)