Agent groups
A group manages several agents that do the same job as one unit. You can spread calls across them, let them call each other, keep their tool access and guardrails the same, and share one monthly budget. A group is a grouping over ordinary agents. Each agent keeps its own credential, traces and audit trail.
A group is one saved configuration. Everything except the name and the agents is an optional section, off until you turn it on. Nothing is applied until you click Save, and one Save applies the whole configuration.
Create a group
Open Groups
Go to Agents and open the Groups tab. Saved groups are listed on the left. Choose New to start a draft.
Name it
The name must be unique in your workspace. The description is optional.
Add agents
Pick at least one agent. Each row shows its spend this month once the group is saved.
Turn on the sections you want
Each section below has its own switch. Leave the rest off.
Click Create group
Later edits use Save. Anyone with the builder role or higher, or a custom role holding
agent_groups:write, can create and edit groups. Viewers can read them. Deleting a group needs an admin.
If a section can't be applied, for example a budget that clashes with another pool, the whole Save fails and nothing changes.
Load balancing
Turn this on to send calls to the group instead of to one agent. Pick a strategy:
| Strategy | How the next agent is chosen |
|---|---|
| Round-robin | Each call goes to the next agent in turn. Calls spread evenly. |
| Weighted | You set a percentage per agent. The percentages must total 100, or the Save is refused. A 70/30 split sends about 70% of calls to the first agent. |
| Failover | Agents are tried top to bottom. Use the up and down arrows to set the order. The first agent takes every call until it can't. |
Calls go through POST /v1/agent-groups/{id}/run. The call
takes exactly the same governed path as running one agent directly, so each
agent's budget, guardrails, skills and tracing still apply. If load balancing
is off, that endpoint returns 409.
When a call moves to the next agent
Every strategy picks an order. If the first agent can't serve the call, Forgebench tries the next one in that order.
The call moves on when:
- the agent's gateway call fails (502);
- the agent has used its own budget (402
agent_budget_exceeded), or its model's monthly or daily budget (402model_budget_exceeded,model_daily_budget_exceeded); - the agent's model isn't allowed for the caller (403
model_not_allowed); - the agent keeps being refused its tools (429
tool_repeatedly_unauthorized); - a budget pool that belongs to that agent alone is used up (402
pool_budget_exceededorpool_member_share_exceeded); - the agent is paused or retired;
- the agent has failed recently and its breaker is open. A success closes it.
The call is returned to you as is when:
- a policy refuses it (any other 403 or 400), for example a guardrail block or a missing permission. Another agent would apply the same policy, so retrying only hides the refusal;
- the budget pool that pays for the call is used up (402
pool_budget_exceeded,pool_member_share_exceeded). That is the group's shared budget when the group has one, and otherwise the caller's own budget pool. Every agent shares it, so another agent would hit the same wall; - the workspace budget is exhausted;
- the request itself is invalid.
If every agent fails, you get the last error. If every agent is paused or
tripped, you get 503 no_member_available with the reason for each.
Internal connect
Turn this on to let the agents in the group call each other (agent-to-agent). Forgebench creates a permission for every ordered pair of members, so a group of four gets 12.
Granting a permission is changing what the calling agent may do, so you can only create the pairs whose caller you own (or any, if you're an admin or owner). A pair whose caller you don't own gets no permission at all; an admin saving the group fills it in.
The agent being called must approve too. If you also speak for it (admin, or its owner), the permission is approved at once. If not, it is created as pending until the callee's owner approves it.
Turning it off, or removing an agent from the group, revokes only the
permissions the group created. Permissions someone granted by hand are left
alone. Saving the group again never undoes a revoke made by an owner or an
admin: only a permission the group revoked comes back when you turn the
switch on again, and one that had been approved needs a fresh approval unless
you can approve for the callee. Each permission granted or revoked leaves its
own audit entry, agent.call_binding.granted or agent.call_binding.revoked,
marked via: agent_group.
Primary agent
Pick one member as the primary (the star icon on its row). The other agents copy its configuration. Choose what to copy:
- Inherit MCP access. Each other agent gets the same tool grants as the primary. Tools an agent already had on its own are kept. If the primary loses a tool, the copy is revoked. That includes copies made with Sync all from the primary while inheritance is on. A copy an owner or admin revoked by hand stays revoked.
- Inherit guardrails. Each other agent takes the primary's guardrail
switch, auto-pause settings and per-check policies. This overwrites their
own guardrail settings. The review flag (
guardrail_review) is not copied: it is runtime state set when an agent is held for review, not configuration.
The primary must be a member. Inheritance needs a primary.
Who can turn it on
Inheritance changes other agents' access, so saving it needs ownership of
every other member (or the admin or owner role). If you don't own one, the
Save fails with 403 agent_owner_or_admin_required. Inheriting guardrails also
needs the guardrails:write permission (or admin), or the Save fails with 403
guardrails_write_required. The same applies to Sync all.
When copies update
The copies re-sync when you save the group, when you add an agent to it, and whenever someone changes the primary's guardrails or tool access, including revoking a whole MCP server or its stale tools. Each re-sync is logged in Recent activity.
A re-sync after an edit is limited to what both people could do by hand:
the person who made the edit, and the person who last saved the group with
inheritance on (the group remembers them). It writes only to agents that both
can change, with guardrails needing guardrails:write from both, and the
person who saved it must still be an active member who can change the
primary. Agents it skips are not changed, and the edit itself still succeeds.
The skipped agents are listed in the sync event in Recent activity. A group
saved by a key with no identity never re-syncs after an edit, because there is
nobody to check; the copies from the Save itself remain. An approved change
plan re-syncs on the saver's authority alone.
What stays when you turn it off
Turning inheritance off, or deleting the group, stops further syncing. It does not remove what was already copied. The matrix labels copied grants as inherited until you remove them.
MCP access
The MCP access table lists every MCP tool any member can use, against each agent. A cell is one of:
- granted: the agent was given the tool directly;
- inherited: copied from the primary or by Sync all;
- missing: the agent can't use it.
Sync all to an agent copies that agent's MCP access to every other member, in one step. It copies MCP tool grants only, not guardrails. Use it to bring the group in line without a primary. It's disabled while you have unsaved changes, so save first. The source agent must be a member.
Shared budget
Optional. Turn on Share a monthly budget, choose whether it's counted in
dollars or tokens, then set the monthly limit and optional alert thresholds
(percent; the default is 50, 75, 90, 95, 100). A token budget counts prompt and
completion tokens, like a token pool. The unit
is fixed once the budget exists: to switch, turn the budget off and on again
(a Save in the other unit fails with 422 budget_unit_fixed).
All the agents' credentials draw on one cap. When it's used up, calls from any
of them are stopped.
A run of a member through POST /v1/agents/{id}/run or the group's /run is
charged to the group's pool. A call is charged to one pool, because each
usage record names the single pool it was charged to and the pool's counters are
rebuilt from those records. So for a member run the group's pool takes
precedence: it stops the call even when the caller's key sits in a pool of its
own, and the caller's own pool is not charged for that run. The usage record
names the group's pool and the agent's key that was charged, so the counters
rebuild to exactly the live figures. Runs of agents outside a budgeted group are
unaffected.
- Admins only. Builders get 403
admin_requiredif they try to change the budget. They can still rename the group and change its agents, and the pool follows. Adding or removing an agent changes its spend cap, so a builder must own each agent they add or remove (an admin can add any). Otherwise the Save fails with 403pool_member_owner_required, listing the agents. - An agent with no credential of its own has no share in the pool. Its runs are charged as an ungrouped agent's are, not to another member's share.
- Behind the scenes this is an agent budget pool. It is
named
Group: <name>. Changing the limit or the agents updates the pool. - An agent whose credential is already in another pool blocks the Save with
409
keys_in_other_pool, and the error names the keys and pools. Remove them from that pool first. - Turning the section off archives the pool. The agents go back to their own budgets.
With no shared budget, the group still shows what its agents spent this month. That's information only, and it caps nothing.
Recent activity
The panel under the form lists the latest events, newest first:
- Served by an agent: each run, with the outcome and how many agents were tried. A run refused by policy shows as refused. A run that no agent could serve shows as failed.
- MCP sync: a sync from the primary or from Sync all.
- Config change: a create or update.
Recording an event never blocks a call. If the write fails, the call still completes.
Run a group
curl https://api.forgebench.ai/v1/agent-groups/$GROUP_ID/run \
-H "Authorization: Bearer $FORGEBENCH_KEY" \
-H "Content-Type: application/json" \
-d '{"message": "Summarise this invoice"}'The request body is the same as for running one agent: message is required, and top_k, dimensions, stream and metadata are optional. The credential
needs write access to agents. The response is that agent's usual run response
plus three fields:
| Field | Meaning |
|---|---|
agent_group_id | The group that took the call. |
served_by_agent_id | The agent that answered. |
attempts | How many agents were tried. Paused and tripped agents are skipped and not counted. |
The usual fields (answer, model, citations, token counts, cost_usd,
trace_id) come from the agent that served the call.
A streamed call has no JSON body to hold them. Read these response headers
instead: x-agent-group-id, x-served-by-agent-id and x-attempts.
Configuration API
All endpoints are under /v1/agent-groups. They return 404 until Agent Groups
is enabled for your deployment.
| Method and path | Role | Does |
|---|---|---|
GET /v1/agent-groups | Viewer | Lists groups with agent count, the member agent ids, strategy and whether they have a budget. |
POST /v1/agent-groups | Builder | Creates a group from a full configuration. Returns 201. |
GET /v1/agent-groups/{id} | Viewer | One group with per-agent spend, the MCP matrix and budget status. |
PUT /v1/agent-groups/{id} | Builder | Replaces the whole configuration. Sending the same configuration again changes nothing. |
DELETE /v1/agent-groups/{id} | Admin | Deletes the group, revokes the permissions it made and archives its pool. Copied MCP and guardrail settings stay. Returns 204. |
GET /v1/agent-groups/{id}/events | Viewer | Recent activity. limit is 1 to 200, default 50. |
POST /v1/agent-groups/{id}/mcp-sync | Builder | Body {"from_agent_id": ...}. Copies that agent's MCP access to the rest. |
POST /v1/agent-groups/{id}/run | Builder | Runs the group. See above. |
A save body has name, description, members (each with agent_id,
weight and position), load_balancing ({"strategy": ...} or null),
internal_connect, primary_agent_id, inherit (mcp, guardrails) and
budget (monthly_limit_usd or monthly_limit_tokens, alert_thresholds, or null). The response's budget has limit_unit and the figures in that unit: monthly_limit_usd, spent_usd, remaining_usd, or monthly_limit_tokens, spent_tokens, remaining_tokens.
Errors
Errors carry a code in detail.
| Status | Code | Meaning |
|---|---|---|
| 409 | agent_in_other_group | An agent already belongs to another group. The error lists them. |
| 409 | group_name_taken | Another group has that name. |
| 409 | keys_in_other_pool | Shared budget: an agent's key is already in another pool. |
| 409 | load_balancing_off | You called /run on a group with load balancing off. |
| 403 | admin_required | Only admins can set or change a shared budget. |
| 403 | pool_member_owner_required | A builder added or removed an agent they don't own in a group with a shared budget. Lists the agents. |
| 403 | agent_owner_or_admin_required | Inheritance or Sync all would change an agent you don't own. Lists the agents. |
| 403 | guardrails_write_required | Inheriting guardrails needs the guardrails:write permission. |
| 404 | agent_not_found | A member agent doesn't exist. |
| 422 | agent_retired | A retired agent can't join. |
| 422 | duplicate_member | The same agent is listed twice. |
| 422 | weights_must_total_100 | Weighted weights don't add up to 100. |
| 422 | primary_not_member | The primary isn't one of the members. |
| 422 | primary_required | Inheritance is on but there is no primary. |
| 422 | agent_not_member | from_agent_id isn't in the group. |
| 503 | no_member_available | Every agent was paused or tripped on a run. |

