Installation
Configuration
ai configuration and fails at startup without one. It adds its fields to your auth collection. With several auth collections, it uses admin.user; otherwise set authCollection. Users of other auth collections get no model list, budget or spend tracking. When your app has no auth collection, the plugin adds a users collection with a name field and the policy fields. It runs with order: 10.
Fields
monthlyBudget and spendThisPeriodUSD are USD Money fields.
An entry in models is an exact match: allowing an alias allows calls to the alias, not direct calls to the model behind it.
Define a field with the same name on your auth collection to change it. Your field replaces the plugin’s settings it sets, and its admin and access are merged over the plugin’s. For example, let only admins change budgets:
Enforcement
The plugin wraps eachai.access rule (generate, embed, transcribe, rerank, evaluate). For each call, your own rule runs first; without one, the user must be signed in. When it allows a signed-in user, a non-empty models list must include the model or alias the caller asked for. Otherwise the call fails with a 403 and the code model_not_allowed: Model "<id>" is not allowed by this policy. Over HTTP, this returns { "error": { "message": "...", "type": "invalid_request_error", "code": "model_not_allowed", "param": "model" } }, the same body as a spent budget; on /api/v1/messages it is Anthropic’s { "type": "error", "error": { "type": "permission_error", "message": "...", "param": "model" } }.
The same check shapes chat. A user sees only the agent models they may use, and an agent with none is hidden from them. GET /api/v1/models likewise lists only the models in a non-empty models list. Chat shows a microphone only when the user may use the transcription model, and a read aloud button only when they may use the speech model; for an alias, list the model behind it.
The budget is checked when a call runs, before every AI call a signed-in user makes, agent turns included. When monthlyBudget is set and spendThisPeriodUSD has reached it, the call fails with a 403 and the code budget_exceeded: The configured budget has been exhausted. A spent budget doesn’t hide agents or models: chat lists them as usual and shows this error when the user sends. Calls without a signed-in user, such as scheduled tasks, have no per-user limit.
Spend
After each AI call by a signed-in user, the plugin adds its cost to that user’sspendThisPeriodUSD, priced from the model catalog or your custom model prices. Failed calls and calls that cost nothing add nothing. If the spend update fails, FrogBot logs the error and the call still succeeds.
Spend updates for one user run one at a time within a process. Calls already running when the budget is reached still finish, and several server processes can update the same user at once, so treat the budget as a limit that can be exceeded by a few calls, not an exact cap.
Monthly reset
Theplugin-ai-policy-reset-spend task sets every user’s spendThisPeriodUSD to 0 at midnight on the first day of each month (0 0 1 * *), on its own plugin-ai-policy-reset-spend queue. Run a worker that handles schedules for that queue, such as one started with --all-queues --handle-schedules.
API key limits
With the API keys plugin on the same auth collection, each key gets its own limits, inside its owner’s. List the plugins in any order.
The key’s create dialog has a collapsed Limits section with the model list and cap, and the keys list shows spend and cap after the prefix. A key’s models and cap are checked against its owner when they change, for admins too:
"<id>" isn't allowed for <owner>. and Can't exceed <owner>'s monthly budget of $50.00. Values that didn’t change aren’t checked again, so lowering the owner’s budget doesn’t block renaming a key.
Only a signed-in session can change a key’s limits. A request authenticated by an API key that changes models or monthlyBudget on any key gets a 403: API keys can't change their own limits. Sign in to change them.
On a request made with a key, the owner’s model list and budget are checked first, then the key’s:
- a model outside a non-empty key list fails with a
403, codemodel_not_allowed:Model "<id>" is not allowed for API key "<name>".Chat and agent lists, andGET /api/v1/models, show only the key’s models; - a spent cap fails with a
403, codebudget_exceeded:API key "<name>" has used its monthly cap of $25.00.
Use your own policy
The plugin is built only onai.access and AI hooks, so you can replace it with your own rules, such as prepaid credits. Leave the plugin out and put your check in ai.access. Each rule receives { req, model }; return false for a generic 403, or throw an error with a numeric status and a code to send your own:
afterOperation hook that prices the call’s usage with calculateUsageCostUSD({ model, providers, usage }) from frogbot and updates the user.