> ## Documentation Index
> Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sub-agents

> Creating child agents from inside an agent, enumerating them from the platform record, and ending them — spawn, children, and despawn.

An agent can create other agents. A parent mints a child per unit of work,
talks to it as an ordinary typed stub, asks the platform which children it
has, and ends one when the work it represented is over.

The parent keeps no registry of its own. The parent→child relationship is
recorded by the platform when the child is created, so `children()` answers
correctly even for a parent that crashed immediately after spawning and came
back remembering nothing.

<Note>
  Available from SDK **0.15.0**. `spawn`, `children`, and `despawn` are new
  inherited members, so a subclass that already declares a member by one of
  those names must rename it or align its signature to override.
</Note>

## spawn()

> `protected` **spawn**\<`T`>(`namespace`, `name?`): `Promise`\<[`ActorStub`](/docs/edge-compute/stateful-actors/api-reference/stub) & `PublicMethods`\<`T`>>

Create a child instance of an actor binding and return its stub — one
worker per job, minted from inside the agent that needs it.

The child is a **deliberately created** instance owned by this agent, not
one materialized by addressing a name. It is the same stub
`binding.idFromName(...)` hands back, typed against the bound class, so
calling it is ordinary actor RPC:

```ts theme={null}
const child = await this.spawn(this.env.WORKER, `w-${job.id}`);
await child.assign(job); // typed, and dispatched over actor RPC
```

Omit `name` and the child gets a fresh, collision-free one; keep the
returned stub's `id` if you need to reach that child again later.

`name` is a routing name in your own vocabulary — an order id, an E.164
number, an email — and is encoded into an addressable actor id for you, by
the same encoder the front door uses, so a name and its child agree on one
instance no matter which side minted it. A name with no faithful id (empty,
or one that encodes too long to address) is refused **here**, before
anything is created, with a `TypeError` naming the name and the limit: a
child under an unaddressable id would take writes and then fail to come
back the first time it is evicted.

Room is finite. When this agent is already at its cap the platform refuses,
and the refusal arrives as a [QuotaExceededError](/docs/agent-sdk/api-reference/agent/sub-agents#quotaexceedederror) you can catch at
this call site — nothing is created when it throws:

```ts theme={null}
try {
  const child = await this.spawn(this.env.WORKER, `w-${job.id}`);
  await child.assign(job);
} catch (err) {
  if (err instanceof QuotaExceededError) return this.shed(job);
  throw err;
}
```

Added with SDK 0.15 — a new inherited member, so a subclass that already
declares `spawn` must rename it (or align its signature to override this
method).

**Type Parameters**

| Type Parameter                                                                                 |
| ---------------------------------------------------------------------------------------------- |
| `T` *extends* [`StatefulActor`](/docs/edge-compute/stateful-actors/api-reference/base)\<`Env`> |

**Parameters**

| Parameter   | Type                                                                                 | Description                                                      |
| ----------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| `namespace` | [`ActorNamespace`](/docs/edge-compute/stateful-actors/api-reference/namespace)\<`T`> | The actor binding to create the child in — `this.env.<BINDING>`. |
| `name?`     | `string`                                                                             | The child's routing name. Defaults to a fresh unique name.       |

**Returns**

`Promise`\<[`ActorStub`](/docs/edge-compute/stateful-actors/api-reference/stub) & `PublicMethods`\<`T`>>

The child's stub, typed against the bound actor class.

**Throws**

`TypeError` when `name` has no addressable actor id, or when the
binding cannot create instances.

**Throws**

[QuotaExceededError](/docs/agent-sdk/api-reference/agent/sub-agents#quotaexceedederror) when creating the child would exceed a
limit already reached.

## children()

> `protected` **children**(): `Promise`\<[`ChildRef`](/docs/agent-sdk/api-reference/agent/sub-agents#childref)\[]>

The children this agent has, as the platform records them.

The list is read from the platform every time, not from anything this
agent wrote down. That is the point: a parent that crashed after creating
three workers and came back remembering nothing still enumerates all
three, because the record of who belongs to whom never lived in the
parent's state in the first place. There is no registry to keep in sync,
and none to lose.

Children can be of different types, so what comes back are descriptors
rather than stubs — one class cannot type them all. Each carries the name
the child is addressed by, so turning one into a live handle is the
ordinary binding call:

```ts theme={null}
for (const child of await this.children()) {
  if (child.type !== "Worker") continue;
  await this.env.WORKER.idFromName(child.name).ping();
}
```

An agent with no children gets an empty array, not an error.

Added with SDK 0.15 — a new inherited member, so a subclass that already
declares `children` must rename it (or align it to override this method).

**Returns**

`Promise`\<[`ChildRef`](/docs/agent-sdk/api-reference/agent/sub-agents#childref)\[]>

Every child attached to this agent, of every type.

## ChildRef

> `readonly` **createdAt**: `Date`

When the child was created.

***

**name**

> `readonly` **name**: `string`

The child's address — the same string `env.<BINDING>.idFromName(...)`
takes, so a descriptor becomes a callable stub without translation.

***

**status**

> `readonly` **status**: `string`

The child's lifecycle status as the platform reports it.

***

**type**

> `readonly` **type**: `string`

The actor type the child runs as, e.g. `"Worker"`.

## despawn()

> `protected` **despawn**(`child`): `Promise`\<`void`>

End one child of this agent — both halves, in one call.

A child that is finished has to be emptied *and* removed, and doing only
one leaves a mess of a specific kind. Emptying alone leaves the name alive:
the next call to it activates a blank agent that answers as if it were new.
Removing alone leaves everything the child accumulated — its state, its
pending timers, its history — sitting in storage under a name nothing can
reach again. `despawn()` does both, in the order that works: the child
empties itself first, while it can still be reached, and only then is the
name ended. Afterwards a call to the old address is refused rather than
answered by a ghost.

```ts theme={null}
async close(caseId: string): Promise<void> {
  await this.despawn(`case-${caseId}`);
}
```

Pass the name, or the stub you were handed when the child was created —
either identifies the same child. A stub is read for the child it names,
not for how to reach it: the child is addressed by the type the platform
says it runs as, so a stub minted from the wrong binding still ends the
right child rather than emptying one actor and removing another. A name
that is not one of this agent's
children is not an error: it is already in the state being asked for, so
the call returns having done nothing. That makes a retry after a partial
failure safe.

Only this agent's own children can be ended this way. Naming somebody
else's child, or an instance that was never a child at all, does nothing.

Added with SDK 0.15 — a new inherited member, so a subclass that already
declares `despawn` must rename it (or align it to override this method).

**Parameters**

| Parameter | Type                                                                             | Description                              |
| --------- | -------------------------------------------------------------------------------- | ---------------------------------------- |
| `child`   | `string` \| [`ActorStub`](/docs/edge-compute/stateful-actors/api-reference/stub) | The child to end: its name, or its stub. |

**Returns**

`Promise`\<`void`>

<Warning>
  Ending a child is not reversible. The platform records the identity as
  deleted, and a later attempt to create a child under the same name is
  refused rather than quietly returning a fresh, empty agent. Choose child
  names you will not need to reuse — a job id rather than a bare customer
  reference that may come round again.
</Warning>

## QuotaExceededError

Thrown when the platform refuses to create a new actor instance because a
limit is already reached — the parent's cap on how many children it may
hold, or an account-wide cap on instances.

It is an ordinary application error, raised at the call site that asked for
the instance, so it can be caught, logged, and reported like any other:

```ts theme={null}
try {
  const worker = await this.spawn(this.env.WORKER, `w-${job.id}`);
  await worker.assign(job);
} catch (err) {
  if (err instanceof QuotaExceededError) {
    // Out of room — shed the job rather than failing the whole batch.
    await this.queue("retryLater", job, { delaySeconds: 60 });
    return;
  }
  throw err;
}
```

Match it with `instanceof`, or with `err.name === "QuotaExceededError"`
where the error crossed a boundary that rebuilt it from its name.

Nothing was created when this is thrown: the refusal happens before the
instance exists, so there is no half-made child to clean up. Retrying the
same call only succeeds once room is freed.

**Extends**

* `Error`

**Constructors**

| Constructor                                                                      | Description |
| -------------------------------------------------------------------------------- | ----------- |
| [constructor](/docs/agent-sdk/api-reference/agent/sub-agents#quotaexceedederror) | -           |

**Properties**

| Property                                                        | Description                                                      |
| --------------------------------------------------------------- | ---------------------------------------------------------------- |
| [code](/docs/agent-sdk/api-reference/agent/sub-agents#code)     | The machine-readable code from the refusal, when it carried one. |
| [detail](/docs/agent-sdk/api-reference/agent/sub-agents#detail) | The human-readable detail from the refusal, when it carried one. |
| [status](/docs/agent-sdk/api-reference/agent/sub-agents#status) | The HTTP status the refusal arrived with.                        |

### constructor

> **new QuotaExceededError**(`args`): [`QuotaExceededError`](/docs/agent-sdk/api-reference/agent/sub-agents#quotaexceedederror)

**Parameters**

| Parameter      | Type                                                             | Description                                                                                                                                                      |
| -------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `args`         | \{ `code?`: `string`; `detail?`: `string`; `status`: `number`; } | `status` is the HTTP status of the refusal; `code` and `detail` are the machine-readable code and human-readable text it carried, either of which may be absent. |
| `args.code?`   | `string`                                                         | -                                                                                                                                                                |
| `args.detail?` | `string`                                                         | -                                                                                                                                                                |
| `args.status`  | `number`                                                         | -                                                                                                                                                                |

**Returns**

[`QuotaExceededError`](/docs/agent-sdk/api-reference/agent/sub-agents#quotaexceedederror)

**Overrides**

`Error.constructor`

## code

> `readonly` **code**: `string` | `undefined`

The machine-readable code from the refusal, when it carried one.

## detail

> `readonly` **detail**: `string` | `undefined`

The human-readable detail from the refusal, when it carried one.

## status

> `readonly` **status**: `number`

The HTTP status the refusal arrived with.
