Skip to Content
API referencePOST /v1/agents/children

POST /v1/agents/children

Mint a short-lived, scope-attenuated child identity for a subagent. See Child agents for the concept and its caveats; this page is the wire format.

Most integrations don’t call this directly. The Python SDK’s issue_child and the MCP issue_child_agent tool wrap it. Direct invocation is for the Node SDK today (see Node & TypeScript) or a custom integration.

Who can call this

Only an active, non-child agent. A child agent (one with its own parent_agent_id set) cannot mint a child of its own: v1 caps delegation depth at 1.

Request

POST /v1/agents/children HTTP/1.1 Host: api.agentvalet.ai Authorization: Bearer <parent-agent-rs256-jwt> Content-Type: application/json
{ "name": "research-worker", "ttl_seconds": 600, "grants": [ { "platform": "github", "scopes": ["github:repo.read"], "connection_id": null } ] }
FieldTypeRequiredNotes
namestringno1–200 chars. Stored as "<name> (child)". Defaults to child of <parent_agent_id>
ttl_secondsintegerno603600. Defaults to 900. Also caps the grant expiry, see below
grantsarrayyes1–25 entries. Each is { platform, scopes, connection_id? }
grants[].platformstringyesPlatform id, e.g. github
grants[].scopesstring arrayyesThe scopes you’re asking for. Attenuated against the parent’s current grants, see below
grants[].connection_idstring | nullnoPin a specific connection when the parent holds more than one per-connection grant for that platform

Attenuation, at mint time

For each grants[] entry, the proxy reads the parent’s own current permissions row(s) for that platform (the same connection-selection logic checkPermission uses: per-connection rows win outright over a platform-wide row when any exist). The child’s actual scopes are the intersection of what you asked for with what that row currently grants. require_approval and connection_id are copied from the parent’s row, not from the request.

The child’s grant expires_at is min(parent grant's expires_at, mint time + ttl_seconds): a child grant can never outlive the parent grant it was sliced from.

Success: 201 Created

{ "child_agent_id": "agt_9f3a1c2b7e401928d7ee", "bearer_token": "eyJhbGciOi...", "expires_at": "2026-08-21T13:15:00.000Z", "granted": [ { "platform": "github", "scopes": ["github:repo.read"], "require_approval": false } ] }
FieldNotes
child_agent_idThe new agent’s id. Every call it makes is attributed to this id in the audit log
bearer_tokenAn AS-minted access token, valid for ttl_seconds. This is the child’s entire credential; hand it to the subagent and nothing else
expires_atWhen the child (and its bearer) stops being valid
grantedThe attenuated grants actually written for the child, per platform

Error codes

StatuserrorCause
400invalid_namename present but not a 1–200 char string
400grants[] is requiredgrants missing, empty, or not an array
400invalid_grantsMore than 25 entries, or an entry with a malformed platform/scopes
400ttl_seconds must be 60..3600ttl_seconds outside the allowed range
400no_scopes_after_attenuationThe parent has no active, unexpired grant for that platform/connection, or none of the requested scopes survive the intersection. platform is included in the body
401Missing Bearer token | Invalid JWT | Unknown agentSame as POST /v1/actions
403agent_not_activeThe parent agent isn’t active (suspended, revoked, expired)
403delegation_depth_exceededThe caller is itself a child agent. Depth is capped at 1 in v1
409child_limitThe parent already has 25 live children
500issue_failedThe child row or its grants failed to write. No child is left half-created; a partially written row is revoked rather than returned
503as_signing_unavailableThe proxy couldn’t mint a bearer (signing key unconfigured). Fails closed: no child row is left active without a usable bearer

Audit

The mint writes one audit_log row against the parent agent with action: "agent.child_issued" and metadata carrying the new child_agent_id, the granted slice, and the expiry, so the lineage survives even after the child row is eventually garbage-collected.

Every call the child subsequently makes writes its own audit rows under the child’s agent_id, not the parent’s.

See also

  • Child agents: the concept, attenuation semantics, and the honest caveats on stdio Task-tool subagents and approval replay
  • LangGraph: a full supervisor/worker example
  • POST /v1/actions: what the child does with its bearer
Last updated on