:::info Executable tutorial
This page is generated from a tested Jupyter notebook. Open it in Google Colab or view the source notebook.
:::
Share credits across a team
A Bursar team owns a shared credit pool while each member retains an independently enforced spend cap. This tutorial creates a team, manages membership, charges the shared pool, and verifies that membership changes do not reset spend history.
Learning objectives
After completing this tutorial, you can:
- Create and fund a team account
- Add members with individual spend caps
- Charge usage against the shared pool
- Remove and restore membership without losing audit history
Prerequisites
- Complete the credit lifecycle tutorial
- Start the notebook server from
samples/python/notebooks/
Setup
Each notebook starts a throwaway Postgres cluster, runs the bursar schema, and publishes the demo configuration. publish_config returns a Bursar facade bound to the store; team operations live on the store and on bursar.credits.
import atexit
from shared import start_postgres_store, cleanup, base_config, publish_config, USER_ADA, USER_ALEX, USER_JAMAL
from decimal import Decimal
from bursar.metrics import UsageMetrics
store, pgdata = start_postgres_store()
atexit.register(cleanup, pgdata)
bursar = publish_config(store, base_config())
credits = bursar.credits
print("config published")
Creating a team
create_team(owner_subject_id, name, initial_balance) provisions the pool and makes the owner its first member with an uncapped role. get_team_balance reports the pool balance and the number of members.
team = store.create_team(
owner_subject_id=USER_ADA,
name="Acme",
initial_balance=Decimal("1000"),
idempotency_key="team:acme:create",
)
print("team_id:", team.team_id)
balance = store.get_team_balance(team.team_id)
print("balance:", balance.balance)
print("members:", balance.member_count)
Adding members with spend caps
add_team_member(team_id, subject_id, role, spend_cap) adds a member with a cap measured in credits. get_team_members returns each member's role, cap, and cumulative spend.
store.add_team_member(team.team_id, USER_ALEX, role="member", spend_cap=Decimal("200"))
store.add_team_member(team.team_id, USER_JAMAL, role="admin", spend_cap=Decimal("300"))
for member in store.get_team_members(team.team_id):
print(member.user_id[:8], member.role, "cap:", member.spend_cap, "spent:", member.total_spent)
Spending against the team pool
credits.deduct_team(team_id, user_id, metrics, idempotency_key=...) prices the usage with the member's plan rate card, debits the team pool (not the member's personal balance), and attributes the charge to the member. The caller-stable key makes retries replay-safe. The result carries the pool balance after the charge.
bursar.accounts.on_account_created(USER_ALEX, "acct_alex")
result = credits.deduct_team(
team.team_id,
USER_ALEX,
UsageMetrics(
operation="completion",
measures={"input_tokens": Decimal(40000), "output_tokens": Decimal(10000)},
dimensions={"model": "gpt-4o"},
),
idempotency_key="team:alex:completion:1",
)
print("charged:", result.amount)
print("team balance after:", result.team_balance_after)
print("entry_id:", result.entry_id)
for member in store.get_team_members(team.team_id):
print(member.user_id[:8], "spent:", member.total_spent)
Enforcing a member spend cap
A member whose cumulative spend plus the requested charge would exceed their cap is rejected with CapReachedError; the team pool is untouched.
try:
credits.deduct_team(
team.team_id,
USER_ALEX,
UsageMetrics(
operation="completion",
measures={"input_tokens": Decimal(1000000), "output_tokens": Decimal(100000)},
dimensions={"model": "gpt-4o"},
),
idempotency_key="team:alex:cap-check:1",
)
except Exception as exc:
print(type(exc).__name__, str(exc)[:80])
print("balance unchanged:", store.get_team_balance(team.team_id).balance)
Membership lifecycle
remove_team_member revokes access; the member's historical spend is preserved, so re-adding them with a new cap starts from their existing cumulative spend. The final cell always stops the cluster.
print("removed:", store.remove_team_member(team.team_id, USER_JAMAL))
store.add_team_member(team.team_id, USER_JAMAL, role="member", spend_cap=Decimal("500"))
print("members after re-add:", len(store.get_team_members(team.team_id)))
print("all done")