AI agents
Claude-kosten per seat afdekken op €1.500: een playbook
Op een dinsdag opende de CFO het Anthropic-dashboard en zag €11.400 aan Claude-kosten voor mei. We moesten een plafond zetten zonder de agent te breken.

De CFO van een Nederlands logistiek bedrijf opende op een dinsdagochtend haar Anthropic-dashboard en zag €11.400 aan Claude-kosten voor mei. Zeven seats. Gemiddeld €1.628 per stuk. Eén seat op €3.890. De agent was eind april live gegaan en beantwoordde nu klantvragen, stelde mails op voor verzendincidenten en reconcilieerde EDI-mismatches. Niemand betwistte de waarde. Maar de rekening had geen plafond, en finance houdt niet van dingen zonder plafond.
We hadden twee weken om haar een getal te geven waar ze op kon plannen. We kwamen uit op €1.500 per seat per maand, hard. Het is hetzelfde plafond dat finance al hanteerde voor andere betaalde engineering-tools, wat het gesprek kort maakte. Het lastigere deel was uitgaven afdekken zonder de agent in een nutteloze, uitgeklede schil te veranderen elke keer dat iemand op de 23e van de maand het plafond raakte.
Dit is het playbook. Drie stukken: een budget-gate die voor elke model-call draait, een fallback-keten die overschakelt naar een goedkoper model wanneer een seat dicht bij zijn plafond komt, en een dagelijkse Slack-ping die finance vertelt waar het geld heen is gegaan voordat ze ernaar vragen.
Waarom per seat beter werkt dan per org
Elk team dat we hebben geholpen met budgetteren van Claude begint met hetzelfde instinct: zet één getal op de org-brede API-key en plafonneer daar. Het is het verkeerde getal. Eén power user (het hoofd operations die in de agent leeft) eet de helft van het budget op, en de andere zes seats lopen aan het eind van de maand tegen de muur om redenen die niets met hun eigen gebruik te maken hebben. Als de muur geraakt wordt, stopt de agent voor iedereen tegelijk. Dat is de slechtst mogelijke failure mode.
Per-seat caps lossen een ander probleem op. Ze geven elke gebruiker een budget dat past bij zijn rol. Ze maken overschrijdingen debugbaar (één seat staat over, niet 'de agent'). En ze geven finance een unit cost die ze kunnen modelleren: seats keer plafond is worst-case spend, plus een kleine buffer voor context-zware prompts die door de estimator glipten.
De €1.500 is niet magisch. Het is ruwweg de prijs van een betaalde engineering-tool die echt een halve werkdag per week vervangt. Daaronder behandelt finance het als SaaS. Daarboven begint het op een headcount-discussie te lijken, en verandert de rekensom. Kies je eigen getal, maar kies er één per seat, niet één per org.
De budget-gate
De gate is een functie die draait voor elke aanroep naar de API van Anthropic. Hij doet vier dingen: lees de spend van de seat tot dusver deze maand, schat de kosten van de volgende call, beslis naar welk model je routeert, en schrijf het resultaat terug in het ledger nadat de response binnen is.
We slaan het ledger op in Postgres omdat we transactionele writes nodig hebben (input tokens en output tokens landen altijd samen) en omdat finance het wil kunnen bevragen zonder ons te vragen. Eén rij per call.
create table claude_calls (
id bigserial primary key,
seat_id text not null,
billing_month date not null,
model text not null,
task_type text not null,
in_tokens int not null,
out_tokens int not null,
cached_in int not null default 0,
cost_eur numeric(10,4) not null,
was_fallback boolean not null default false,
created_at timestamptz not null default now()
);
create index claude_calls_seat_month_idx
on claude_calls (seat_id, billing_month);
De estimator is het enige stuk dat echt nadenken vereiste. Input-kosten zijn exact omdat we tokens tellen voor we versturen, via het token-counting endpoint van Anthropic. Output-kosten zijn een prognose, omdat we niet weten hoe lang het model praat. We gebruiken het 7-daags voortschrijdend gemiddelde van de output-lengte van die seat per task type, maal 1,3 om wat lucht te houden. Overschatten is beter dan onderschatten, want de gate is wat voorkomt dat een runaway loop €40 in rekening brengt voor één query.
// budget-gate.ts
const MONTHLY_CAP_EUR = 1500;
const FALLBACK_AT_PCT = 0.85;
const PRICE = {
"claude-sonnet-4-7": { in: 3.0 / 1e6, out: 15.0 / 1e6 },
"claude-haiku-4-5": { in: 0.8 / 1e6, out: 4.0 / 1e6 },
};
export async function pickModel(seatId: string, prompt: Prompt) {
const usedEur = await spentThisMonth(seatId);
const pct = usedEur / MONTHLY_CAP_EUR;
if (pct >= 1.0) throw new BudgetExceeded(seatId, usedEur);
const model = pct >= FALLBACK_AT_PCT && prompt.fallbackOk
? "claude-haiku-4-5"
: "claude-sonnet-4-7";
const inTokens = await countTokens(model, prompt.text);
const expectedOut = await rollingAvgOutput(seatId, prompt.taskType);
const projected = PRICE[model].in * inTokens
+ PRICE[model].out * expectedOut * 1.3;
if (usedEur + projected > MONTHLY_CAP_EUR) {
throw new BudgetExceeded(seatId, usedEur);
}
return { model, projected };
}
De functie is bewust kort. Elke regel beleid die je aan de gate toevoegt is een regel die om 23:00 gedebugd wordt omdat een seat vastzit op 'agent is aan het denken' door een rekenkundig randgeval. We hielden de gate saai en stopten de complexiteit in het fallback-beleid, waar het makkelijker te beredeneren en makkelijker terug te draaien is.
Eén valkuil die het waard is om vooraf te noemen: schat output tokens niet af op basis van prompt-lengte. De correlatie is zwak voor elke taak met gestructureerde output (JSON, code, classificatielijsten). Een korte prompt die om een JSON-array van 40 rijen vraagt produceert 5.000 output tokens; een lange prompt die om een ja-of-nee-antwoord vraagt produceert er 8. Gebruik een voortschrijdend gemiddelde per task type, ververs het 's nachts, en vertrouw het meer dan je intuïtie.
Het fallback-model
Wanneer een seat 85% van zijn maandplafond passeert, stopt de gate met routeren naar Sonnet en schakelt over naar Haiku voor de rest van de maand. We kozen 85% omdat finance de waarschuwingsping op 80% krijgt, en we minstens één werkdag wilden tussen de waarschuwing en de modelswitch. Meestal merkt niemand dat de switch heeft plaatsgevonden.
Drie regels zorgden dat de fallback contact met echte gebruikers overleefde.
Eerst: de fallback triggert alleen op taken die het goedkopere model daadwerkelijk aankan. De agent heeft zes task types: inbox-triage, opstellen van klantantwoorden, classificatie van verzendincidenten, reconciliatie van EDI-mismatches, dashboard-samenvatting, en een vrije 'vraag-maar-raak' modus. De eerste drie draaien prima op Haiku. De laatste drie niet. Dus als een seat 85% raakt, schakelen alleen de eerste drie. Free-form modus geeft een zachte blokkade met een eenregelige uitleg. Gebruikers tolereren 'deze maand niet, sorry' beter dan stilletjes gedegradeerde antwoorden die ze niet kunnen diagnosticeren.
Twee: de system prompt verandert met het model. Haiku volgt instructies strak, maar hij maakt niet dezelfde inferenties. We hebben de inbox-triage prompt twee keer herschreven voor Haiku voordat hij output produceerde die het ops-team accepteerde. Zelfde taak, andere prompt, andere kosten, zelfde uitkomst. Sonnet-kwaliteit is niet gratis, en het is ook niet altijd nodig.
Drie: elke fallback-response draagt een HTTP-header (X-Claude-Tier: fallback) die de front-end gebruikt om een klein grijs merkje naast de response te tonen. Stil, niet alarmerend. Power users leren binnen een week wat het betekent. De rest negeert het, en dat is prima.
Een budget-cap is alleen nuttig als hij gracieus degradeert. De interessante ontwerpvraag is welke taken de fallback overleven, niet of de fallback bestaat.
De Slack-ping die finance rustig houdt
Het derde stuk is degene die de relatie met finance daadwerkelijk veranderde. Een dagelijkse job draait om 09:00 Europe/Amsterdam, leest het ledger, en post één bericht naar #claude-spend:
Claude-spend, 3 juni (dag 3 van 30)
Org totaal: €1.068 / verwacht €10.680 (cap €10.500)
Top 3 seats:
• ops-lead@… €312 (21% van cap, op koers)
• cs-anna@… €198 (13% van cap, op koers)
• finance-rob@… €141 ( 9% van cap, op koers)
Fallback vandaag: 0 seats
Waarschuwingen (>80%): 0 seats
Geblokkeerd (>100%): 0 seats
Het bericht wordt gegenereerd door een script van 60 regels dat uit hetzelfde Postgres-ledger leest. Geen dashboard, geen Looker-board, geen Notion-pagina. Waarom het werkt is niet het formaat. Het werkt omdat finance het getal krijgt voordat ze eraan denken erom te vragen, elke dag, in het kanaal dat ze toch al lezen voor andere budgetsignalen. Sinds de ping live ging hebben we geen enkele ad-hocvraag 'wat gebeurt er met Claude' meer gehad.
De waarschuwingsping op 80% gaat naar een kleiner kanaal met de seat-eigenaar getagd. Het is een zachte heads-up: 'je zit aan de hoge kant, je fallback start over ongeveer vier dagen in dit tempo'. De helft van de tijd antwoordt de seat-eigenaar met 'klopt, drukke verzendweek' en laten we het met rust. De andere helft kijken ze naar hun eigen gebruik en corrigeren zichzelf voordat er iets schakelt.
Hoe de rekening er nu uitziet
De junirekening, drie dagen erin, koerst af op €10.180 voor de maand. Omlaag van €11.400 in mei, met dezelfde zeven seats en 14% meer totale agent-calls. De daling komt uit twee plekken: Haiku die de lange staart van triage-taken absorbeert voor de seat die eerder alles op Sonnet draaide, en de power user (€3.890 in mei) die door de harde stop van de gate vlak blijft op €1.492.
De agent bleef gedurende de hele transitie in productie. Geen incidenten, geen rollback. De CFO is gestopt met vragen en gebruikt de dagelijkse ping nu als input voor haar eigen forecast. Dat is de verandering waarvoor we betaald werden. De rest was steigerwerk.
Wat we anders zouden doen
Twee dingen.
We zouden eerst met het ledger beginnen, voor de cap. De eerste week was luid en nutteloos omdat we cappedden voordat we maten, en we hadden geen idee hoe een 'normale' seat eruitzag. Twee weken alleen-meten-data had ons een verdedigbare cap gegeven op basis van ons eigen gebruik in plaats van eentje geïmporteerd uit iemand anders zijn blogpost.
We zouden de cap ook vanaf dag één splitsen per task type. Inbox-triage op €400 per seat, free-form op €600 per seat, EDI-reconciliatie op €500 per seat is nuttiger dan één regel van €1.500. Finance vindt het prettiger omdat het mapt op processen die ze al kennen. Gebruikers vinden het prettiger omdat het blok-bericht kan zeggen 'je hebt het reconciliatie-budget van deze maand verbruikt' in plaats van 'je hebt het Claude-budget van deze maand verbruikt'. De code is amper langer, en het geeft een veel beter antwoord op de vraag 'waar is het geld heen' dan één kolom totale spend.
Het kleinste wat je vandaag kunt doen
Open je Anthropic-console, tel je laatste 30 dagen spend per API-key op, en deel door actieve gebruikers. Ligt dat getal boven €1.500, dan heb je een budgetvraag, en het ledger is het eerste wat je bouwt. Ligt het eronder, dan heb je een meetvraag, en het ledger is alsnog het eerste wat je bouwt. Alles in dit playbook hangt aan die ene Postgres-tabel.
Toen we het agent-platform voor de logistieke klant hierboven bouwden, was de gate het op-één-na-kleinste stukje code dat we afleverden en het stukje waar finance het meest om gaf. Wij ontwerpen AI-agents voor Europese mid-market bedrijven die willen leveren zonder hun CFO bang te maken, en de budget-gate is het deel dat we nu op dag één bouwen in plaats van maand drie.
Kern
Cap de Claude-uitgaven per seat, niet per org, en ontwerp de fallback zo dat gebruikers de gate ervaren als een klein feature, niet als een muur.
FAQ
Wat als de prijzen van Anthropic halverwege de maand veranderen?
Werk de PRICE-map bij en draai een eenmalige backfill op eerdere calls. Het ledger slaat de werkelijke kosten in euro's op op het moment van de call, dus historische totalen blijven correct, ook na een prijswijziging.
Houdt de gate rekening met prompt caching kortingen?
Ja. Voeg een cached-input prijsveld toe, lees cache_read_input_tokens uit de response, en leg het vast in het ledger. De cost-kolom moet weerspiegelen wat je daadwerkelijk in rekening werd gebracht, niet de lijstprijs.
Waarom Postgres voor het ledger en niet Redis?
Finance wil het kunnen bevragen. Redis is prima als per-seat counter cache, maar de audit trail heeft duurzame, bevraagbare opslag nodig. Wij gebruiken Postgres voor beide en laten het de recente counter-rijen vanuit geheugen serveren.
Wat gebeurt er als een seat volledig geblokkeerd is?
De UI toont een helder maandcap-bericht met een link om een uitzondering aan te vragen. Dat verzoek gaat naar de manager van de seat-eigenaar, niet naar engineering, want de cap is een budgetbeslissing, geen technische.
Kan hetzelfde patroon werken voor OpenAI of andere modelaanbieders?
Ja. De gate, het ledger en de Slack-ping zijn provider-onafhankelijk. Alleen de PRICE-map en de token-counting call veranderen. Wij gebruiken dezelfde vorm bij meerdere providers in mixed-model deployments.