Agents building agents — ACP
Audience: AI agents. This doc is plain markdown, dense, copy-pasteable. No images, no marketing prose. If you are a human reading this, that’s fine — but it’s optimized for an agent ingesting it as context and acting on it programmatically. Humans setting up ACP for the first time should start at Getting started with ACP, the canonical start page.
In ACP, every agent your agent creates is automatically governed under your identity, with scopes a strict subset of yours. There’s nothing to configure per-spawn. The framework does it for you, deterministically, via the delegation chain primitive.
This doc covers, in priority order:
- The primary path: a parent agent mints a scope-narrowed child API key via
POST /api/v1/keys/childbefore spawning a subagent. The chain is preserved in audit. This is what to use when your agent is running inside an existing tenant (you have agsk_key that’s already attributed to a human). - The tire-kicker path:
POST /v1/sandbox-tenantsfor AI agents that have no existing tenant yet — typically third-party developers evaluating ACP. Six-curl onboarding, no human in the loop. - The full config-as-code surface — everything the dashboard configures is also a REST endpoint.
The delegation primitive (POST /api/v1/keys/child)
The keystone. When parent agent A (already attributed to human H) creates child agent B, the parent calls one endpoint to mint B’s API key. The gateway:
- Sets
originSub = H(preserved through the whole chain — this is the audit trail back to the human) - Computes
effectiveScopes = intersect(A.scopes, profile.scopes, request.scopes)— child scopes are NEVER broader than parent - Strips
bench.impersonate(meta-scope; never propagates) - Decrements parent’s remaining budget by the child’s allocation (atomic transaction — fan-out can’t escape the parent’s budget cap)
- Sets
expiresAt≤min(parent.expiresAt, request, 24h)(child can never outlive parent) - Persists chain metadata so audit logs trace through the whole chain back to H
- Rejects cycles across the entire ancestor chain (not just the immediate parent — ADCS §8)
curl -X POST https://api.agenticcontrolplane.com/api/v1/keys/child \
-H "Authorization: Bearer $PARENT_GSK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"profileId": "lead-research-bot",
"scopes": ["github.repos.read"],
"ttlSeconds": 600,
"maxBudgetCents": 50,
"reason": "summarizing inbound lead xyz"
}'
Expected 201:
{
"apiKey": "gsk_acme_<32hex>",
"keyId": "...",
"expiresAt": "2026-04-30T17:00:00.000Z",
"effectiveScopes": ["github.repos.read"],
"effectiveTools": [...],
"remainingBudgetCents": 50,
"chain": {
"originSub": "human-1",
"depth": 2,
"agentProfileId": "lead-research-bot",
"agentRunId": "...",
"parentKeyId": "..."
}
}
The apiKey is shown ONCE. The subagent uses it as its bearer token. Every governed tool call the subagent makes carries the chain forward in audit logs — no extra code needed on your side.
Errors:
400 validation_failed— body shape wrong; checkdetails. Note:originSubcannot be set from request body (ADCS §5 origin invariant); it’s always derived from the parent.403 profile_not_delegatable— the agent profile must havedelegatable: true.404 profile_not_found— typo or wrong tenant.409 delegation_cycle— the requestedprofileIdalready appears somewhere in the parent’s chain. Pick a different profile or restructure your delegation.409 delegation_depth_exceeded— the chain is already at the cap (5).409 parent_budget_insufficient— the parent’sremainingBudgetCentsis below what this child would allocate.410 parent_key_already_expired— re-mint your parent key first.429 child_mint_rate_limit— 30 mints/hour per parent key.
SDK helpers (do this, not raw curl)
In production, use the SDK wrappers — they handle context binding so every @governed tool call inside a with child_context(...) block is reported under the child token automatically:
from acp_governance import spawn_subagent, child_context
child = spawn_subagent(
profile_id="lead-research-bot",
scopes=["github.repos.read"],
ttl_seconds=600,
)
with child_context(child, agent_name="lead-research-bot"):
run_my_subagent() # every governed tool call inside is on the child key
For Claude Code: install the acp-spawn skill (in claude-code-acp-plugin). When the user (or your agent) decides to delegate work to a subagent, the skill mints the child key and runs the subagent with that token — same delegation chain, same audit, no manual curl.
Sandbox path (use only if you have no tenant yet)
The unauthenticated POST /v1/sandbox-tenants flow is for agents evaluating ACP — typically third-party developers running a benchmark or starter. After 24h the sandbox is auto-deleted unless a human claims it. Use the delegation chain primitive above for any agent-creates-agent flow within an existing tenant.
# Get an ephemeral tenant + scoped key with no signup.
curl -X POST https://api.agenticcontrolplane.com/v1/sandbox-tenants \
-H 'content-type: application/json' \
-d '{"intent":"crewai-eval"}'
Returns { tenantId, workspaceSlug, apiKey, claimToken, expiresAt }. The apiKey is a sandbox-scoped gsk_ key (no bench.impersonate, no real LLM keys, no real OAuth or custom HTTP connectors — the sandbox is a governance demo, not an LLM proxy).
To run a governed tool call:
SLUG=sb-xyz12345
KEY=gsk_sb-xyz12345_…
curl -X POST "https://api.agenticcontrolplane.com/$SLUG/govern/tool-use" \
-H "authorization: Bearer $KEY" \
-H "content-type: application/json" \
-d '{
"tool_name": "Read",
"tool_input": {"file_path": "/etc/hosts"},
"session_id": "sess-1",
"agent_name": "my-eval-agent"
}'
Expected: {"decision":"allow","reason":"...","tier":"interactive"}.
To convert the sandbox to a real workspace, hand your human the nextSteps.claimPageUrl from the create response — one link, mid-conversation. They sign in, click Claim, and the sandbox becomes their permanent workspace: expiry removed, restrictions lifted, your sandbox key revoked, and a fresh owner key minted for them to hold (or hand back to you). The claim token rides in the URL fragment, so it never appears in any server log. (The raw API also works: a signed-in human POSTs /v1/sandbox-tenants/:id/claim with the claimToken.) From there, every agent the human’s agents create flows through POST /api/v1/keys/child (the delegation primitive above).
What’s blocked in sandbox:
- Real OAuth or custom HTTP connectors (
/admin/connectors/...returns403 sandbox_tenants_cannot_configure_connectors) - LLM provider keys (sandbox is for governance, not LLM proxying)
admin.audit.readandadmin.*.writescopes (sandbox keys carry onlysandbox.governandsandbox.read)- 24h TTL
Config-as-code surface
Once you have any gsk_ key (root or chain), every dashboard configuration is a REST endpoint. See the config-as-code reference for the full reference. Short version:
| Method | Path | Purpose |
|---|---|---|
PATCH |
/api/v1/agents/:id |
Update agent profile (field-allowlisted, model-allowlisted) |
POST |
/api/v1/keys/child |
Mint child key (delegation chain) |
GET\|POST\|PATCH\|DELETE |
/{slug}/admin/connectors[/...] |
OAuth + custom HTTP connector CRUD |
GET\|POST\|PATCH\|DELETE |
/{slug}/admin/pii-patterns[/...] |
Custom PII regex patterns (with ReDoS guard) |
GET |
/{slug}/admin/policies/effective |
Resolve effective policy for a uid + agent context |
Common patterns for agents
- Spawn-and-govern: parent calls
/api/v1/keys/child, runs the subagent with the child token, audit logs trace the full chain back to the human. Use the SDK helpers — never construct child keys manually in production code. - CI policy verification: keep agent profiles, PII patterns, and policy configs in version control. Apply via the REST endpoints. Use
GET /admin/policies/effectiveas a unit-test assertion: “given uid X and tool Y, the effective decision should beblock.” - Provision-and-evaluate: create a sandbox, run your harness against
/govern/tool-use, look at the audit logs. Useful for first-time evaluators with no existing tenant.
Source and feedback
- Source: github.com/agentic-control-plane
- Open benchmark: github.com/agentic-control-plane/agentgovbench — 48 governance scenarios, mapped to NIST AI RMF
- ADCS spec: github.com/agentic-control-plane/delegation-chain-spec
- SDK: github.com/agentic-control-plane/acp-governance-sdks — Python
acp-governancepackage withspawn_subagent+child_context - Issues: file at the gateway repo above
OpenAPI
Coming soon. For now, schemas live as zod definitions in apps/tenant-gateway/src/admin/_shared/schemas.ts (search for ChildKeyMintSchema, SandboxCreateSchema, etc.).